How can I serialize/deserialize a dictionary with custom keys using Json.Net?

后端 未结 3 722
误落风尘
误落风尘 2020-11-28 13:15

I have the following class, that I use as a key in a dictionary:

    public class MyClass
    {
        private readonly string _property;

        public My         


        
相关标签:
3条回答
  • 2020-11-28 13:39

    This should do the trick:

    Serialization:

    JsonConvert.SerializeObject(expected.ToArray(), Formatting.Indented, jsonSerializerSettings);
    

    By calling expected.ToArray() you're serializing an array of KeyValuePair<MyClass, object> objects rather than the dictionary.

    Deserialization:

    JsonConvert.DeserializeObject<KeyValuePair<IDataKey, object>[]>(output, jsonSerializerSettings).ToDictionary(kv => kv.Key, kv => kv.Value);
    

    Here you deserialize the array and then retrieve the dictionary with .ToDictionary(...) call.

    I'm not sure if the output meets your expectations, but surely it passes the equality assertion.

    0 讨论(0)
  • 2020-11-28 13:43

    Grx70's answer is good - just adding an alternative solution here. I ran into this problem in a Web API project where I wasn't calling SerializeObject but allowing the serialization to happen automagically.

    This custom JsonConverter based on Brian Rogers' answer to a similar question did the trick for me:

    public class DeepDictionaryConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (typeof(IDictionary).IsAssignableFrom(objectType) ||
                    TypeImplementsGenericInterface(objectType, typeof(IDictionary<,>)));
        }
    
        private static bool TypeImplementsGenericInterface(Type concreteType, Type interfaceType)
        {
            return concreteType.GetInterfaces()
                   .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Type type = value.GetType();
            IEnumerable keys = (IEnumerable)type.GetProperty("Keys").GetValue(value, null);
            IEnumerable values = (IEnumerable)type.GetProperty("Values").GetValue(value, null);
            IEnumerator valueEnumerator = values.GetEnumerator();
    
            writer.WriteStartArray();
            foreach (object key in keys)
            {
                valueEnumerator.MoveNext();
    
                writer.WriteStartArray();
                serializer.Serialize(writer, key);
                serializer.Serialize(writer, valueEnumerator.Current);
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    In my case, I was serializing a Dictionary<MyCustomType, int> property on a class where MyCustomType had properties like Name and Id. This is the result:

    ...
    "dictionaryProp": [
        [
          {
            "name": "MyCustomTypeInstance1.Name",
            "description": null,
            "id": null
          },
          3
        ],
        [
          {
            "name": "MyCustomTypeInstance2.Name",
            "description": null,
            "id": null
          },
          2
        ]
    ]
    ...
    
    0 讨论(0)
  • 2020-11-28 13:43

    Simpler, full solution, using a custom JsonConverter

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public class CustomDictionaryConverter<TKey, TValue> : JsonConverter
    {
        public override bool CanConvert(Type objectType) => objectType == typeof(Dictionary<TKey, TValue>);
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            => serializer.Serialize(writer, ((Dictionary<TKey, TValue>)value).ToList());
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            => serializer.Deserialize<KeyValuePair<TKey, TValue>[]>(reader).ToDictionary(kv => kv.Key, kv => kv.Value);
    }
    

    Usage:

    [JsonConverter(typeof(CustomDictionaryConverter<KeyType, ValueType>))]
    public Dictionary<KeyType, ValueType> MyDictionary;
    
    0 讨论(0)
提交回复
热议问题