具有多态子对象的类型的 Json.Net 序列化 [英] Json.Net Serialization of Type with Polymorphic Child Object

查看:16
本文介绍了具有多态子对象的类型的 Json.Net 序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们希望能够从/向 C# 类序列化/反序列化 json,主类具有多态子对象的实例.使用 Json.Net 的 TypeNameHandling.Auto 设置很容易做到这一点.但是,我们希望在没有$type"字段的情况下这样做.

We would like to be able to serialize/deserialize json from/to C# classes, with the main class having an instance of a polymorphic child object. Doing so is easy using Json.Net's TypeNameHandling.Auto setting. However, we would like to do so without the "$type" field.

第一个想法是能够将$type"重命名为我们选择的值,并使该类型的值是一个可以正确映射子类型的枚举.我还没有将其视为一种选择,但很高兴听到是否可行.

The first thought is to be able to rename "$type" to a value of our choosing and to have the value for the type be an enum that would map the sub-types properly. I have not seen that as being an option but would be glad to hear if it is possible.

第二个想法是沿着以下几行...下面是类的第一遍,顶级类有一个指示符 (SubTypeType),指示子对象 (SubTypeData) 中包含什么类型的数据.我仔细研究了 Json.Net 文档并尝试了一些方法,但没有成功.

The second thought was along the following lines... Below is a first pass at classes, with the top level class having an indicator (SubTypeType) as to what type of data is contained in the child object (SubTypeData). I've dug around a bit into the Json.Net documentation and have tried a few things but have had no luck.

我们目前可以完全控制数据定义,但是一旦部署,事情就会被锁定.

We currently have full control over the data definition, but once it is deployed, then things are locked.

public class MainClass
{
  public SubType          SubTypeType { get; set; }
  public SubTypeClassBase SubTypeData { get; set; }
}

public class SubTypeClassBase
{
}

public class SubTypeClass1 : SubTypeClassBase
{
  public string AaaField { get; set; }
}

public class SubTypeClass2 : SubTypeClassBase
{
  public string ZzzField { get; set; }
}

推荐答案

在容器类中有子类型信息是有问题的,原因有两个:

Having the subtype information in the container class is problematic for two reasons:

  1. 当 Json.NET 正在读取包含的类时,容器类实例不可访问.
  2. 如果您以后需要将 SubTypeClassBase 属性转换为一个列表,那么子类型信息将无处可放.
  1. The container class instance is not accessible when Json.NET is reading the contained class.
  2. If you later need to convert the SubTypeClassBase property into, say, a list, there will be nowhere to put the subtype information.

相反,我建议在基类中添加子类型信息作为属性:

Instead, I would recommend adding the subtype information as a property in the base class:

[JsonConverter(typeof(SubTypeClassConverter))]
public class SubTypeClassBase
{
    [JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value
    public SubType Type { get { return typeToSubType[GetType()]; } }
}

现在,每当可分配给 SubTypeClassBase 的对象被序列化时,自定义子类型枚举将被序列化.完成后,对于反序列化,您可以创建一个 JsonConverter 将给定 SubTypeClassBase 的 json 加载到临时 JObject,检查 "Type" 属性的值,并将 JSON 对象反序列化为适当的类.

Now the custom subtype enum will be serialized whenever an object assignable to SubTypeClassBase is serialized. Having done that, for deserialization you can create a JsonConverter that loads the json for a given SubTypeClassBase into a temporary JObject, checks the value of the "Type" property, and deserializes the JSON object as the appropriate class.

原型实现如下:

public enum SubType
{
    BaseType,
    Type1,
    Type2,
}

[JsonConverter(typeof(SubTypeClassConverter))]
public class SubTypeClassBase
{
    static readonly Dictionary<Type, SubType> typeToSubType;
    static readonly Dictionary<SubType, Type> subTypeToType;

    static SubTypeClassBase()
    {
        typeToSubType = new Dictionary<Type,SubType>()
        {
            { typeof(SubTypeClassBase), SubType.BaseType },
            { typeof(SubTypeClass1), SubType.Type1 },
            { typeof(SubTypeClass2), SubType.Type2 },
        };
        subTypeToType = typeToSubType.ToDictionary(pair => pair.Value, pair => pair.Key);
    }

    public static Type GetType(SubType subType)
    {
        return subTypeToType[subType];
    }

    [JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value
    public SubType Type { get { return typeToSubType[GetType()]; } }
}

public class SubTypeClass1 : SubTypeClassBase
{
    public string AaaField { get; set; }
}

public class SubTypeClass2 : SubTypeClassBase
{
    public string ZzzField { get; set; }
}

public class SubTypeClassConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(SubTypeClassBase);
    }

    public override bool CanWrite { get { return false; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var typeToken = token["Type"];
        if (typeToken == null)
            throw new InvalidOperationException("invalid object");
        var actualType = SubTypeClassBase.GetType(typeToken.ToObject<SubType>(serializer));
        if (existingValue == null || existingValue.GetType() != actualType)
        {
            var contract = serializer.ContractResolver.ResolveContract(actualType);
            existingValue = contract.DefaultCreator();
        }
        using (var subReader = token.CreateReader())
        {
            // Using "populate" avoids infinite recursion.
            serializer.Populate(subReader, existingValue);
        }
        return existingValue;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

这篇关于具有多态子对象的类型的 Json.Net 序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆