Short version: I have json input with numerical properties. Is there a way to make liquid respect the types of my json input? The template seems committed to treating my numbers as strings.
We're implementing liquid templates using Fluid.Core in c#. We've got most things working, but we got to the point of creating templates and found numerical comparisons to not work with < or > operators without an explicit cast into variables before comparing. I set up a minimal example to show our problem:
Running the code:
TemplateOptions.Default.MemberAccessStrategy = new UnsafeMemberAccessStrategy();//we want property access
var jObject = JObject.Parse(testSettings.templateContextJson);
TemplateContext tempContext = new TemplateContext(jObject);
var temp = fluidParser.Parse(template);
logger.Info(temp.Render(tempContext));
with a template
{% assign smol = smallerVal | plus: 0 %}assigning {{smallerVal}} to smol
{% assign big = greaterVal | plus: 0 %}assigning {{greaterVal}} to big
using the assigned vals: '{{ big > smol }}'
using the native properties: '{{ greaterVal > smallerVal }}'
and data
{
"smallerVal": 300.00,
"greaterVal": 400.00
}
produces the results
assigning 300 to smol
assigning 400 to big
using the assigned vals: 'true'
using the native properties: ''
Apparently the native properties version of > is comparing them as strings? But when we make them explicitly numerical by adding 0 the comparisons start to work. This is fine, we can work with it, but is there any way to convince the liquid template parser to treat numbers as numbers without forcing everyone to do explicit casts beforehand?
Digging in to the object I can see that the TemplateContext object has correctly stored the values as numeric. The below screenshot shows '300' and '400' in memory without quotes, so liquid/fluid are maintaining the correct type up until this point.
Digging into the template output I can confirm it definitely thinks the variables are strings though. Is there any way to force liquid/fluid to respect the original typing of the json properties?
Addendum: I wanted to test some of my assumptions, so I wrote some more code. Can confirm liquid sees the vars as strings.
public ValueTask<FluidValue> LiquidToType(FluidValue input, FilterArguments arguments, TemplateContext context)
{
if (input.IsNil())
return new StringValue(input?.ToStringValue() ?? "null");
else
return new StringValue(input.GetType().ToString());
}
//then where we set the unsafemember strategy we also add this
TemplateOptions.Default.Filters.AddFilter("toType", LiquidToType);
We then setup this template
{% assign smol = smallerVal | plus: 0 %}assigning {{smallerVal}} to smol
{% assign big = greaterVal | plus: 0 %}assigning {{greaterVal}} to big
using the assigned vals: '{{ big > smol }}'
using the native properties: '{{ greaterVal > smallerVal }}'
{% assign smol2 = smallerVal %}assigning {{smallerVal}} to smol2
{% assign big2 = greaterVal %}assigning {{greaterVal}} to big2
using unconverted variables: '{{ big2 > smol2 }}'
Types of the three variables:
Native: {{ smallerVal | toType }}
Cast: {{ smol | toType }}
Variable: {{ smol2 | toType }}
and the results:
assigning 300 to smol
assigning 400 to big
using the assigned vals: 'true'
using the native properties: ''
assigning 300 to smol2
assigning 400 to big2
using unconverted variables: ''
Types of the three variables:
Native: Fluid.Values.StringValue
Cast: Fluid.Values.NumberValue
Variable: Fluid.Values.StringValue
So it's definitely the typing issue. Somehow our numerical values are treated as strings by default. Is there no way to alter the default setting inside our template to not do that? Like they are literally doing a cast to turn it into a string from the templateContext.
Can anyone confirm if I'm asking for the impossible or show me a way to achieve this?
Thanks for the help
