Newtonsoft.json: JsonContainerAttribute Does Not Respect NamingStrategyParameters for Dictionaries

Created on 23 Oct 2019  路  5Comments  路  Source: JamesNK/Newtonsoft.Json

Source/destination types

[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy),
            NamingStrategyParameters = new object[] { true, true })]
public class UsesOverriddenNamingStrategy
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public IDictionary<string, int> ExtraThings { get; set; }
}

Source/destination JSON

// actual value:
{"id":42,"full_name":"John Doe","extra_things":{"ExtraValue":1}}

// expected value:
{"id":42,"full_name":"John Doe","extra_things":{"extra_value":1}}

Steps to reproduce

var foo = new UsesOverriddenNamingStrategy
{
    Id = 42,
    FullName = "John Doe",
    ExtraThings = new Dictionary<string, int>
    {
        ["ExtraValue"] = 1
    }
}

string json = JsonConvert.SerializeObject(foo);

Expected behavior

I expected the dictionary keys to be converted to snake case because the NamingStrategyParameters were true (i.e. ProcessDictionaryKeys == true).

Actual behavior

The property names were overridden (so NamingStrategyType was used) but dictionary keys were not converted. They use the naming strategy from the default contract resolver instead of the one specified in either the JsonObject or JsonProperty override.

Most helpful comment

That's fine and all, but then that probably means that [JsonProperty] shouldn't allow specifying NamingStrategyParameters if they aren't actually going to be used. This is misleading:

public class Foo
{
    [JsonProperty(
      NamingStrategyType = typeof(CamelCaseNamingStrategy), // <-- this will be used
      NamingStrategyParameters = new object[] { true, true })] // <-- but these won't
    public IDictionary<string, int> Bar { get; set; }
}

In that example, the first of those NamingStrategyParameters is specifically related to the handling of dictionary keys, and it's applied to a dictionary, and yet they aren't used. That doesn't really follow the Principle of Least Surprise.

I've added a PR which allows those parameters to be bound to the property so that they will be used during serialization.

You _could_ implement your own class MyDictionary : IDictionary<string, int> and decorate it with [JsonDictionary] to control this behaviour, but then that means you're using different types to influence serialisation settings whereas typically you use [JsonProperty] to influence those regardless of type.

All 5 comments

Applying a naming strategy to a type doesn't automatically make all its child objects also use that strategy. In this case ExtraThings is a child object.

That's fine and all, but then that probably means that [JsonProperty] shouldn't allow specifying NamingStrategyParameters if they aren't actually going to be used. This is misleading:

public class Foo
{
    [JsonProperty(
      NamingStrategyType = typeof(CamelCaseNamingStrategy), // <-- this will be used
      NamingStrategyParameters = new object[] { true, true })] // <-- but these won't
    public IDictionary<string, int> Bar { get; set; }
}

In that example, the first of those NamingStrategyParameters is specifically related to the handling of dictionary keys, and it's applied to a dictionary, and yet they aren't used. That doesn't really follow the Principle of Least Surprise.

I've added a PR which allows those parameters to be bound to the property so that they will be used during serialization.

You _could_ implement your own class MyDictionary : IDictionary<string, int> and decorate it with [JsonDictionary] to control this behaviour, but then that means you're using different types to influence serialisation settings whereas typically you use [JsonProperty] to influence those regardless of type.

That's fine and all, but then that probably means that [JsonProperty] shouldn't allow specifying NamingStrategyParameters if they aren't actually going to be used.

That is the naming strategy applied to that individual property. Yes those parameters will be used. The flag to say it should apply to dictionary keys however doesn't make sense in that context.

You could implement your own class MyDictionary : IDictionary and decorate it with [JsonDictionary] to control this behaviour, but then that means you're using different types to influence serialisation settings whereas typically you use [JsonProperty] to influence those regardless of type.

Yes I think the real problem is not that [JsonProperty] isn't working correctly, but that there seems to be no way to decorate a specific property of type IDictionary<Key,Value> or any other Dictionary for that matter, with a namingStrategy for it's keys.

Because I agree creating new dictionary just so you can set it's Json serialization options is something I'd rather avoid.
In my use case I'm trying to create a library where people can give an object which contains a dictionary of values. What I don't want to do is constrain them to use "JsonFixedDictionary" as a type.

A proposed solution could be that we could also use the JsonDictionaryAttribute on properties so we have control over how that property renders it's keys.

Any news on this? I just spend an hour pulling my hair out, posting on SO before I found this issue that explains it all.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dmoeff picture dmoeff  路  17Comments

Phreak87 picture Phreak87  路  11Comments

MuiBienCarlota picture MuiBienCarlota  路  14Comments

Richard-Payne picture Richard-Payne  路  13Comments

davidfowl picture davidfowl  路  49Comments