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

查看:114
本文介绍了具有多态子对象的类型的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天全站免登陆