Newtonsoft.json: OverrideSpecifiedNames and ProcessDictionaryKeys default value on CamelCasePropertyNamesContractResolver

Created on 22 Nov 2018  ·  3Comments  ·  Source: JamesNK/Newtonsoft.Json

Hi,

I was just wondering whether there is a reason for defaulting the ProcessDictionaryKeys and OverrideSpecifiedNames settings in the CamelCasePropertyNamesContractResolver class to true:
https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs#L49-L50

I recently discovered an issue in a messaging library that uses JSON.NET for json serialization/deserialization and was able to trace it to its use of the above class with the default options. The issue itself resulted in Dictionary changing keys (all keys were camel-cased) after being sent through the message bus.

The decision to make these values true by default on the contract resolver seems unintuitive to me, as it means that serializing and then deserializing dictionaries does not get you back to where you started. Though I am not certain of the behaviour of OverrideSpecifiedNames, I imagine the same might be true when using custom serialization via JsonProperty attributes if deserialization then relies on a custom constructor.

Is there something I am missing?

Thanks,
Alex

Most helpful comment

They're set to true because that has always been the default behavior of CamelCasePropertyNamesContractResolver. I added naming strategies later and decided to make them behave differently, however I couldn't change the resolver for backwards compatibility reasons.

All 3 comments

To reproduce the issue:

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace ConsoleApp
{
    class Program
    {
        static T SerializeAndDeserialize<T>(T obj, JsonSerializerSettings settings)
        {
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj, settings), settings);
        }

        static void Main(string[] args)
        {
            var dict = new Dictionary<string, string> {{"Foo", "Bar"}};

            var settingsWithDictKeysCamelCased = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            };

            var settingsWithDictKeysUnchanged = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new CamelCaseNamingStrategy()
                }
            };

            var result1 = SerializeAndDeserialize(dict, settingsWithDictKeysCamelCased);
            var result2 = SerializeAndDeserialize(dict, settingsWithDictKeysUnchanged);

            Console.WriteLine(string.Join(", ", result1.Select(kvp => $"{kvp.Key}: {kvp.Value}"))); // foo: Bar
            Console.WriteLine(string.Join(", ", result2.Select(kvp => $"{kvp.Key}: {kvp.Value}"))); // Foo: Bar

            Console.ReadLine();
        }
    }

}

I also am seeing this behavior where a public Dictionary<string, bool> Blah { get; } keys are being changed and I've tried setting ProcessDictionaryKeys to false.

They're set to true because that has always been the default behavior of CamelCasePropertyNamesContractResolver. I added naming strategies later and decided to make them behave differently, however I couldn't change the resolver for backwards compatibility reasons.

Was this page helpful?
0 / 5 - 0 ratings