Search code examples
c#json.netjson.net

C# Deserializing a Dictionary<(Enum,Enum),string> with JSON.NET


I've been struggling to deserialize a Dictionary with Enum Tuple keys in Json.Net. The main problem is that the deserializer can't convert the values back to the specified Enum Tuple whether it's the Enum's string or integer representation in the Json.

I've tried these solutions but none of the worked:

  1. https://stackoverflow.com/a/59310390
  2. https://gist.github.com/SteveDunn/e355b98b0dbf5a0209cb8294f7fffe24

Tried to write my own Custom Converter, the WriteJson part works, but I can't even stop the ReadJson function with breakpoints to see what happens inside.

I'd be happy to get some help with the issue. Maybe somebody already has a solution.

The JSON looks like this:

{
   "DictionaryProperty":{
      "(EnumType1Value1, EnumType2Value1)":"New",
      "(EnumType1Value1, EnumType2Value1)":"Old",
      "(EnumType1Value2, EnumType2Value1)":"Newer",
      "(EnumType1Value2, EnumType2Value2)":"Older"
}

This is the error I get:

"Could not convert string '(EnumType1Value1, EnumType2Value1)' to dictionary key type 'System.ValueTuple`2[EnumType1,EnumType2]'"

This is the custom converter I wrote:

public class CustomDictionaryConverter : JsonConverter<Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string>>
{
    public override void WriteJson(JsonWriter writer, Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, ((Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string>)value).ToList());
    }

    public override Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> ReadJson(
        JsonReader reader
        , Type objectType
        , Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> existingValue
        , bool hasExistingValue
        , JsonSerializer serializer)
    {
        Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> tempdict = new Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string>();

        var temp = serializer.Deserialize<Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string>[]>(reader)[0];

        tempdict = (Dictionary<(EnumType1 Enum1, EnumType2  Enum2), string>)temp;
        return tempdict;
    }
}

Solution

  • A bit manual (and lacking decent error checking), but works for your specified json both serializing & deserializing

    public class CustomDictionaryConverter : JsonConverter<Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string>>
    {
        public override void WriteJson(JsonWriter writer, Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> value, JsonSerializer serializer)
        {
            writer.WriteStartObject();
            foreach(var kv in value)
            {
                writer.WritePropertyName($"({kv.Key.Enum1.ToString()},{kv.Key.Enum2.ToString()})");
                writer.WriteValue(kv.Value);
            }
            writer.WriteEndObject();
        }
    
        public override Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> ReadJson(
            JsonReader reader
            , Type objectType
            , Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> existingValue
            , bool hasExistingValue
            , JsonSerializer serializer)
        {
            Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string> tempdict = new Dictionary<(EnumType1 Enum1, EnumType2 Enum2), string>();
            JObject jObject = JObject.Load(reader);
            foreach(var kv in ((IEnumerable<KeyValuePair<string, JToken>>)jObject)) 
            {
                var keys = kv.Key.Replace("(","").Replace(")","").Split(",");
                var key1 = Enum.Parse<EnumType1>(keys[0]);
                var key2 = Enum.Parse<EnumType2>(keys[1]);
                var value = kv.Value.ToString();
                tempdict.Add((key1,key2),value);
            }
            return tempdict;
        }
    }
    

    Live example: https://dotnetfiddle.net/w85HgK