如何序列化从字典派生的类

How To Serialize a class that derives from a Dictionary(如何序列化从字典派生的类)

本文介绍了如何序列化从字典派生的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Json.Net 在 Json 之间序列化/反序列化以下类:

I am attempting to serialize/deserialize the following class to and from Json using Json.Net:

public class ChildDictionary:Dictionary<Employee, double>
{

    public string Name { get; set; }

}

我在这里找到了信息,这里,和 here 是相关的,但没有一个专门处理我们从字典派生的这种情况下的语法应该是什么样的.

I have found the information here, here, and here that are related but none of them deal specifically with what the syntax should look like for this case where we derive from a Dictionary.

Employee 成功地自行使用 Json.Net 进行序列化.它看起来像这样:

Employee successfully serializes with Json.Net on its own. It looks like this:

[JsonObject(MemberSerialization.OptIn)]
public class Employee
{

    [JsonProperty]
    public string Name { get; set; }

    [JsonProperty]
    public double Factor { get; set; }

    [JsonProperty]
    public List<ILoadBuilder> LoadBuilders = new List<ILoadBuilder>();

    [JsonConstructor]
    public LoadCause(string name, double factor, List<ILoadBuilder> loadBuilders)
    {
        this.Name = name;
        this.DurationFactor = Factor;
        this.LoadBuilders = loadBuilders;
    }
}

我不在乎Json到底长什么样子,只要我能读写不丢失数据就行了

I don't care what the Json looks like in the end as long as I can write and read it without losing data

对完成此操作的代码有何建议?自定义 JsonConverter 或 Attributes 都是很好的解决方案.

Any suggestions of what the code to accomplish this should look like? Both a Custom JsonConverter or Attributes are fine solutions.

推荐答案

因为你的字典既有复杂的键又有额外的属性,你需要使用自定义的 JsonConverter 来序列化和反序列化这个类.下面是一个可以完成这项工作的转换器.它分两部分处理序列化:首先它使用反射来处理类上的任何读写属性,然后将对象转换为字典接口来处理键值对.后者作为具有 KeyValue 属性的对象数组写入 JSON,以便管理复杂的键而无需跳过额外的环节.

Because your dictionary has both a complex key and additional properties, you will need to use a custom JsonConverter to serialize and deserialize this class. Below is a converter that should do the job. It handles the serialization in two parts: first it uses reflection to deal with any read-write properties on the class, then it casts the object to a dictionary interface to handle the key-value pairs. The latter are written to the JSON as an array of objects with Key and Value properties so that the complex keys are managed without needing to jump through extra hoops.

public class ComplexDictionaryConverter<K,V> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<K,V>)));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JObject obj = new JObject();
        foreach (PropertyInfo prop in GetReadWriteProperties(value.GetType()))
        {
            object val = prop.GetValue(value);
            obj.Add(prop.Name, val != null ? JToken.FromObject(val, serializer) : new JValue(val));
        }
        JArray array = new JArray();
        foreach (var kvp in (IDictionary<K, V>)value)
        {
            JObject item = new JObject();
            item.Add("Key", JToken.FromObject(kvp.Key, serializer));
            item.Add("Value", kvp.Value != null ? JToken.FromObject(kvp.Value, serializer) : new JValue(kvp.Value));
            array.Add(item);
        }
        obj.Add("KVPs", array);
        obj.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        IDictionary<K, V> dict = (IDictionary<K, V>)Activator.CreateInstance(objectType);
        foreach (PropertyInfo prop in GetReadWriteProperties(objectType))
        {
            JToken token = obj[prop.Name];
            object val = token != null ? token.ToObject(prop.PropertyType, serializer) : null;
            prop.SetValue(dict, val);
        }
        JArray array = (JArray)obj["KVPs"];
        foreach (JObject kvp in array.Children<JObject>())
        {
            K key = kvp["Key"].ToObject<K>(serializer);
            V val = kvp["Value"].ToObject<V>(serializer);
            dict.Add(key, val);
        }
        return dict;
    }

    private IEnumerable<PropertyInfo> GetReadWriteProperties(Type type)
    {
        return type.GetProperties().Where(p => p.CanRead && p.CanWrite && !p.GetIndexParameters().Any());
    }
}

要使用转换器,您可以像这样使用 [JsonConverter] 属性标记您的类(确保泛型参数与您的类继承自的字典的参数匹配):

To use the converter, you can mark your class with a [JsonConverter] attribute like this (be sure the generic parameters match those of the dictionary your class inherits from):

[JsonConverter(typeof(ComplexDictionaryConverter<Employee, double>))]
public class ChildDictionary : Dictionary<Employee, double>
{
    ...
}

这是一个完整的往返演示:

Here is a demo showing a full round-trip:

class Program
{
    static void Main(string[] args)
    {
        ChildDictionary dict = new ChildDictionary();
        dict.Name = "Roster";
        dict.Add(new Employee { Id = 22, Name = "Joe", HireDate = new DateTime(2012, 4, 17) }, 1923.07);
        dict.Add(new Employee { Id = 45, Name = "Fred", HireDate = new DateTime(2010, 8, 22) }, 1415.25);

        string json = JsonConvert.SerializeObject(dict, Formatting.Indented);
        Console.WriteLine(json);

        dict = JsonConvert.DeserializeObject<ChildDictionary>(json);
        Console.WriteLine("Name: " + dict.Name);

        foreach (var kvp in dict)
        {
            Console.WriteLine("Employee Id: " + kvp.Key.Id);
            Console.WriteLine("Employee Name: " + kvp.Key.Name);
            Console.WriteLine("Employee Hire Date: " + kvp.Key.HireDate);
            Console.WriteLine("Amount: " + kvp.Value);
            Console.WriteLine();
        }
    }
}

[JsonConverter(typeof(ComplexDictionaryConverter<Employee, double>))]
public class ChildDictionary : Dictionary<Employee, double>
{
    public string Name { get; set; }
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime HireDate { get; set; }
}

输出:

{
  "Name": "Roster",
  "KVPs": [
    {
      "Key": {
        "Id": 22,
        "Name": "Joe",
        "HireDate": "2012-04-17T00:00:00"
      },
      "Value": 1923.07
    },
    {
      "Key": {
        "Id": 45,
        "Name": "Fred",
        "HireDate": "2010-08-22T00:00:00"
      },
      "Value": 1415.25
    }
  ]
}
Name: Roster
Employee Id: 22
Employee Name: Joe
Employee Hire Date: 4/17/2012 12:00:00 AM
Amount: 1923.07

Employee Id: 45
Employee Name: Fred
Employee Hire Date: 8/22/2010 12:00:00 AM
Amount: 1415.25

小提琴:https://dotnetfiddle.net/fTfoIk

这篇关于如何序列化从字典派生的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:如何序列化从字典派生的类