fluid icon indicating copy to clipboard operation
fluid copied to clipboard

MemberAccessStrategy.IgnoreCase doesn't work

Open apavelm opened this issue 1 year ago • 13 comments

Fluid.Core version 2.5.0

Ignore Case doesn't work, repro-code below. Is there any option to make it case-insensitive?

public string ProcessTemplate(string templateContent, object substitutionObject)
{
    if (Parser.TryParse(templateContent, out var template, out var error))
    {
        var options = new TemplateOptions
        {
            MemberAccessStrategy =
            {
                IgnoreCasing = true
            }
        };
        var context = new TemplateContext(substitutionObject, options);

        return template.Render(context);
    }
    else
    {
        throw new TemplateServiceException("Error processing template: " + error);
    }
}

public void Test()
{
    var template = "Hello, your {VehicleBrand} with LP number: {LicensePlate} ...";
    var data = new Dictionary<string, object>()
    {
        {"VehiclEbrand", "Toyota"},
        {"LicENsePlate", "ab123cd11"}
    };

Console.WriteLine(ProcessTemplate(template, data)); // result - "Hello, your  with LP number: "
}

apavelm avatar Dec 22 '23 21:12 apavelm

@sebastienros Is it "by design", or it is a bug? Please advise.

apavelm avatar Dec 22 '23 21:12 apavelm

Check this

https://github.com/sebastienros/fluid/blob/f62d8a58b2098989bb59b5fca1c404db10b13f70/Fluid.Tests/TemplateTests.cs#L870-L890

hishamco avatar Dec 22 '23 22:12 hishamco

Can you add a test case that show it works if you type the correct case {{ p.Firstname }} ?

sebastienros avatar Dec 22 '23 22:12 sebastienros

@hishamco I didn't see that was you. Ignore my comment.

@apavelm your template is not valid, it needs two curly braces: {{ VehiclEbrand }}

sebastienros avatar Dec 22 '23 22:12 sebastienros

No problem Seb :)

@apavelm your template is not valid, it needs two curly braces: {{ VehiclEbrand }}

I know there should be a unit test for it, that's why I let @apavelm discover his bug :)

hishamco avatar Dec 22 '23 22:12 hishamco

@sebastienros Brackets somehow missed from the template here, of course there are double-brackets in the template. Sorry for confusion. The full version of the template looks like:

var template = @"Your {% if VehicleBrand != ""Other"" %} {{VehicleBrand}} {% endif %} {{LicensePlate}} ..."

It works when the case matches, and doesn't work if not , ignoring IgnoreCase setting. Also, could it be the reason of usage Dictionary<string, object> instead of object?

@hishamco on a provided sample you register the type, maybe this is the reason - I don't know. But for my case, anonymous types are essential.

P.S. To confirm the issue before posting it here, I created 2 identical test cases with small difference in Letter-case. And as I wrote above 1 - pass OK (when case matches) , 2 - doesn't (VehicleBrand -> VehiclEbrand)

apavelm avatar Dec 23 '23 05:12 apavelm

For your convinience:


public string ProcessTemplate(string templateContent, object substitutionObject)
{
    if (Parser.TryParse(templateContent, out var template, out var error))
    {
        var options = new TemplateOptions
        {
            MemberAccessStrategy =
            {
                IgnoreCasing = true
            }
        };
        var context = new TemplateContext(substitutionObject, options);

        return template.Render(context);
    }
    else
    {
        throw new TemplateServiceException("Error processing template: " + error);
    }
}

public void Test()
{
    var template = @"Your {% if VehicleBrand != ""Other"" %} {{VehicleBrand}} {% endif %} {{LicensePlate}} ...";

    var data = new Dictionary<string, object>()
    {
        {"VehicleBrand", "Toyota"},
        {"LicensePlate", "ab123cd11"}
    };
    var dataBroken = new Dictionary<string, object>()
    {
        {"VehiclEbrand", "Toyota"},
        {"LicENsePlate", "ab123cd11"}
    };

Console.WriteLine(ProcessTemplate(template, data)); // result - "Your Toyota ab123cd11 ..."
Console.WriteLine(ProcessTemplate(template, dataBroken)); // result - "Your ..."
}

apavelm avatar Dec 23 '23 06:12 apavelm

Thanks for confirming. I think it's a bug on the custom properties, not the ones coming from the model which are using the membership accessors.

Can you try to pass a dictionary that is initialized with a StringComparison.OrdinalIgnoreCase?

sebastienros avatar Dec 23 '23 06:12 sebastienros

UPDATE. I additionally added a couple of tests using anonymous and a typed object instead of Dictionary - same result.

Then, I tried all the tests with adding the following line into ProcessTemplate function

options.MemberAccessStrategy.Register(substitutionObject.GetType());

After adding that line, test with objects (anonymous and typed) passed respecting the IgnoreCase setting option, but test with Dictionary<string, object> with Keys in incorrect case still fail.

apavelm avatar Dec 23 '23 06:12 apavelm

@sebastienros Can you try to pass a dictionary that is initialized with a StringComparison.OrdinalIgnoreCase?

Sorry, what do you mean? Could you please elaborate.

Actually, I would be happy to pass an anonymous object instead of Dictionary, but for now, I can not. I can try to verify templates and data-models manually. But as you know, "manually" is the word I would like to exclude :-) Thanks.

Looking forward to an updated version.

apavelm avatar Dec 23 '23 06:12 apavelm

var data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
    {
        {"VehicleBrand", "Toyota"},
        {"LicensePlate", "ab123cd11"}
    };

sebastienros avatar Dec 23 '23 17:12 sebastienros

Oh, a test with StringComparer.OrdinalIgnoreCase passes.

Unfortunately, in a real code, Dictionary is coming from JSON a result of deserialization.

For my case, I think I can tune-up a deserializer in this part. So it is a good bypass option. Thank you, but I would appreciate it if it was the part of library.

Happy holidays!

apavelm avatar Dec 24 '23 06:12 apavelm