Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camel-cased property names + dictionary-as-object serialization = lossy conversion #1023

Closed
bwatts opened this issue Sep 15, 2016 · 5 comments

Comments

@bwatts
Copy link

bwatts commented Sep 15, 2016

This issue started as a comment on the ASP.NET MVC issue concerning camel-casing of property names by default.

Below is a copy of the comment I left over there. Is this an issue that should be solved at the level of JSON.NET?

I just encountered an interesting scenario with camel-cased names and wanted to ensure we account for it.

The default serialization for a Dictionary<,> is to a JSON object:

{ "foo": "...", "bar": "..." }

However, if we serialize an upper-cased key Foo, it will become lower-cased as a property, then deserialize back with the lower-cased name, creating a lossy conversion.

We worked around this by creating a DictionaryConverter that treats dictionaries as List<KeyValuePair<K, V>>:

[
    { "key": "Foo", "value": "..." },
    { "key": "bar", "value": "..." }
]

We wanted to simply tell JSON.NET to use JsonArrayContract for dictionaries, but had to account for existing data that uses the object-not-array format.

@JamesNK
Copy link
Owner

JamesNK commented Sep 16, 2016

Yes changing the dictionary key on serialization is lossy. That is by design. That is why I defaulted it to off with NamingStrategy. I believe it is also off by default with MVC Core.

@bwatts
Copy link
Author

bwatts commented Sep 16, 2016

I thought so as well - I read through the source and indeed found the naming strategy bits.

However, here is .NET Fiddle with a reproduction (using Newtonsoft.Json 9.0.1)

Am I misinterpreting the results?

public static void Main()
{
    var x = new Dictionary<string, int>{ { "Foo", 1 }, { "Bar", 2 } };

    var settings = new JsonSerializerSettings();

    settings.ContractResolver = new CamelCasePropertyNamesContractResolver();

    Console.WriteLine(JsonConvert.SerializeObject(x, settings));
}

Results:

{"foo":1,"bar":2}

@bwatts
Copy link
Author

bwatts commented Sep 16, 2016

It appears that CamelCasePropertyNamesContractResolver is simply not the right mechanism for my scenario, as it doesn't respect the dictionary key casing, while DefaultContractResolver has that capability.

I found this out from @khellang over on this thread, where he points out that MVC uses DefaultContractResolver with the CamelCaseNamingStrategy. I will move to using this now.

Given the unexpected effect of CamelCasePropertyNamesContractResolver in this context, and the fact that lossy dictionary conversions probably aren't a value-add, perhaps we could apply [Obsolete] to CamelCasePropertyNamesContractResolver?

@dougbu
Copy link

dougbu commented Sep 18, 2016

@bwatts CamelCasePropertyNamesContractResolver maintains its behaviour from earlier versions. That class explicitly overrides the DefaultContractResolver CamelCaseNamingStrategy defaults.

@JamesNK
Copy link
Owner

JamesNK commented Sep 18, 2016

tl;dr; use CamelCaseNamingStrategy if you don't want dictionary keys touched.

@JamesNK JamesNK closed this as completed Sep 18, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants