3

The following class

public class RequestSections : RequestBase
{
    public RequestSections(Command c, Dictionary<SectionIdentifier, BigInteger> v) : base(c)
    {
        VERSIONS = v;
    }

    public Dictionary<SectionIdentifier, BigInteger> VERSIONS { get; set; }
}

is serialized to JSON using JSON.NET and producing the following JSON output:

{
  "VERSIONS": {
    "Photos": 901,
    "Music": 902
  },
  "CMD": 43
}

The problem is that SectionIdentifier is enum but JSON.NET converts them to string.

public enum SectionIdentifier
{
    Photos = 1000,
    Music
}

How can I prevent JSON.NET converting integer enum values to string? I would like to see their integer representations only.

By the way, CMD which is residing in RequestBase class is also enum type, but somehow luckily it is not converted to string.

Pablo
  • 28,133
  • 34
  • 125
  • 215
  • 2
    You can always just write your own `JsonConverter`. – Jeroen Vannevel Jul 27 '14 at 20:08
  • I was hoping to understand why sometimes it converts to strings and sometimes not, because I read somewhere that JSON.NET converts enums to integers by default. – Pablo Jul 27 '14 at 20:11
  • As @JeroenVannevel mentioned, you can write your own `JsonConverter`. [Here's a blog post](http://gooddevbaddev.wordpress.com/2013/08/26/deserializing-c-enums-using-json-net/) that explains how you might do that. – fourpastmidnight Jul 27 '14 at 20:20
  • You are using enums as keys to your dictionary and not as values. That's why they are converted to strings. – Dejan Jul 27 '14 at 20:22
  • ah, so JSON spec itself preventing me to have non-string keys, right? – Pablo Jul 27 '14 at 20:32
  • Well, you could have numbers as keys but turned into strings (as explained here: http://stackoverflow.com/questions/8758715/using-number-as-index-json) – Dejan Jul 27 '14 at 20:37

1 Answers1

14

The JSON spec says that property names (keys) in objects must be strings. If you have a dictionary that uses enum values as keys, Json.Net simply calls Convert.ToString() on those values to get the JSON property names. (This can be seen in the GetPropertyName() method in the source code, which is called by SerializeDictionary().)

It is possible to override this default behavior so that Json.Net will write numeric enum dictionary keys to the JSON (still as strings, of course, in keeping with the spec). This can be done using either a custom ContractResolver or a custom JsonConverter. The resolver approach will probably be a bit simpler in this particular case, so I'll show that here. Here is the code you would need:

class CustomResolver : DefaultContractResolver
{
    protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
    {
        var contract = base.CreateDictionaryContract(objectType);

        var keyType = contract.DictionaryKeyType;
        if (keyType.BaseType == typeof(Enum))
        {
            contract.PropertyNameResolver = 
                     propName => ((int)Enum.Parse(keyType, propName)).ToString();
        }

        return contract;
    }
}

To serialize, pass an instance of the custom resolver to the serializer via the settings like this:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomResolver();

string json = JsonConvert.SerializeObject(foo, settings);

Here is a contrived demo to show that it works. You can comment out the line that sets the resolver to toggle the behavior.

class Program
{
    static void Main(string[] args)
    {
        var dict = new Dictionary<Color, string>
        {
            { Color.Red, "#FF0000" },
            { Color.Green, "#00FF00" },
            { Color.Blue, "#0000FF" },
            { Color.White, "#FFFFFF" }
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.ContractResolver = new CustomResolver();
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(dict, settings);
        Console.WriteLine(json);
    }

    enum Color { Red = 1, Green = 2, Blue = 3, White = 4 }
}

Output:

{
  "1": "#FF0000",
  "2": "#00FF00",
  "3": "#0000FF",
  "4": "#FFFFFF"
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300