TextCase.SnakeCase deserialization issue

There is some issue while deserializing json with snake_case notation.

Just found it in the 5.13.2 version.

How to reproduce:

    class Dto
    {
        public int? Property { get; set; }
        public int? AnotherProperty { get; set; }
    }

    // Program.Main()
    JsConfig.Init(new Config
    {
        TextCase = TextCase.SnakeCase
    });
    var dto = new Dto { Property = 1, AnotherProperty = 1 };
    var json = dto.ToJson(); // json: {"property": 1, "another_property": 1}
    var anotherDto = json.FromJson<Dto>(); // anotherDto.AnotherProperty is null

The serialization looks good and the property names with capital letters in the middle got “snake_cased”, however while trying to deserialize back, the very same property has no value assigned.

Use:

JsConfig.Init(new ServiceStack.Text.Config {
    TextCase = TextCase.SnakeCase,
    PropertyConvention = PropertyConvention.Lenient,
});
1 Like

Thanks! I even bothered to clone the ServiceStack.Text repo to check why is that so, and found the part responsible for parsing:

        internal static TypeAccessor Get(this KeyValuePair<string, TypeAccessor>[] accessors, ReadOnlySpan<char> propertyName, bool lenient)
        {
            var testValue = FindPropertyAccessor(accessors, propertyName);
            if (testValue != null) 
                return testValue;

            if (lenient)
                return FindPropertyAccessor(accessors, 
                    propertyName.ToString().Replace("-", string.Empty).Replace("_", string.Empty).AsSpan());
            
            return null;
        }

Isn’t that wrong behavior? I assumed, that setting the TextCase.SnakeCase in JsConfig is valid for both: serializing and deserializing. However, the code clearly says the TextCase is only utilized during serialization and in one place during deserialization (when the type is enum). The PropertyConvention on the other hand, is only used during deserialization.

Don’t take me wrong. I’m totally fine having an extra line of code, as you suggested:

JsConfig.Init(new ServiceStack.Text.Config {
    TextCase = TextCase.SnakeCase,
    PropertyConvention = PropertyConvention.Lenient,
});

I actually workarounded the issue by setting DataMember(Name = "another_property") attributes.

I just think, if you already provide such a customization tool for DTO parsing (which is, to be clear, AWESOME! :heart: ) this is unintuitive and TextCase should also be respected during deserialization.

Yeah it was only used for serialization, I’ve changed it to be used during deserialization from the latest v5.13.3 that’s now available on MyGet where you can just do:

JsConfig.Init(new ServiceStack.Text.Config {
    TextCase = TextCase.SnakeCase,
});
2 Likes