C# 如何使用Json.Net对带有自定义键的字典进行序列化/反序列化?

C# 如何使用Json.Net对带有自定义键的字典进行序列化/反序列化?,c#,.net,json,json.net,C#,.net,Json,Json.net,我有以下类,我将其用作字典中的键: public class MyClass { private readonly string _property; public MyClass(string property) { _property = property; } public string Property { get { ret

我有以下类,我将其用作字典中的键:

    public class MyClass
    {
        private readonly string _property;

        public MyClass(string property)
        {
            _property = property;
        }

        public string Property
        {
            get { return _property; }
        }

        public override bool Equals(object obj)
        {
            MyClass other = obj as MyClass;
            if (other == null) return false;
            return _property == other._property;
        }

        public override int GetHashCode()
        {
            return _property.GetHashCode();
        }
    }
我正在运行的测试如下:

    [Test]
    public void SerializeDictionaryWithCustomKeys()
    {
        IDictionary<MyClass, object> expected = new Dictionary<MyClass, object>();
        expected.Add(new MyClass("sth"), 5.2);
        JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
        string output = JsonConvert.SerializeObject(expected, Formatting.Indented, jsonSerializerSettings);
        var actual = JsonConvert.DeserializeObject<IDictionary<MyClass, object>>(output, jsonSerializerSettings);
        CollectionAssert.AreEqual(expected, actual);
    }

这显然是错误的。我怎样才能让它工作?

这应该可以做到:

序列化:

JsonConvert.SerializeObject(expected.ToArray(), Formatting.Indented, jsonSerializerSettings);
JsonConvert.DeserializeObject<KeyValuePair<IDataKey, object>[]>(output, jsonSerializerSettings).ToDictionary(kv => kv.Key, kv => kv.Value);
通过调用
expected.ToArray()
可以序列化
KeyValuePair
对象数组,而不是字典

反序列化:

JsonConvert.SerializeObject(expected.ToArray(), Formatting.Indented, jsonSerializerSettings);
JsonConvert.DeserializeObject<KeyValuePair<IDataKey, object>[]>(output, jsonSerializerSettings).ToDictionary(kv => kv.Key, kv => kv.Value);
JsonConvert.DeserializeObject(输出,jsonSerializerSettings).ToDictionary(kv=>kv.Key,kv=>kv.Value);
在这里,您反序列化数组,然后使用
.ToDictionary(…)
调用检索字典


我不确定输出是否满足您的期望,但肯定它通过了平等断言。

Grx70的答案很好-只是在这里添加了一个替代解决方案。我在一个WebAPI项目中遇到了这个问题,我没有调用
SerializeObject
,而是允许序列化自动进行

这个基于的自定义
JsonConverter
为我实现了以下技巧:

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();
    }
}

更简单、完整的解决方案,使用自定义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);
}
使用Newtonsoft.Json;
使用制度;
使用System.Collections.Generic;
使用System.Linq;
公共类CustomDictionaryConverter:JsonConverter
{
公共覆盖布尔CanConvert(类型objectType)=>objectType==typeof(字典);
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
=>serializer.Serialize(writer,((字典)值).ToList());
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
=>serializer.Deserialize(reader.ToDictionary)(kv=>kv.Key,kv=>kv.Value);
}
用法:

[JsonConverter(typeof(CustomDictionaryConverter<KeyType, ValueType>))]
public Dictionary<KeyType, ValueType> MyDictionary;
[JsonConverter(typeof(CustomDictionaryConverter))]
公共词典;

JSON并不总能很好地处理字典,为什么不重写
MyClass
.ToString()
?为什么这么复杂的字典键?为什么不在类中包含dictionary值,然后用列表替换dictionary?您可以指定预期的输出应该是什么样子吗?因为我很确定复杂的属性名不是JSON的一部分…@TMcKeown,在实际代码中有不同类型的键。我可以实现
ToString()
和相应的类型转换器,但我希望序列化程序能为我完成这项工作…@mason,显然不是。这个解决方案非常好。您也可以通过提供ReadJson方法的实现来完成这项工作吗?谢谢但是如何反序列化它呢。我也希望看到反序列化。这里有一个要点,有反序列化:太好了,谢谢。注意--
value
WriteJson
中可以为null,所以我添加了一个小检查--
if(value==null)序列化程序;else serializer.Serialize(writer,((字典)值).ToList())