具有多态子对象的类型的Json.Net序列化 [英] Json.Net Serialization of Type with Polymorphic Child Object
问题描述
我们希望能够从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:
- 当Json.NET读取包含的类时,无法访问容器类实例.
- 如果以后需要将
SubTypeClassBase
属性转换为列表,将无处放置子类型信息.
- The container class instance is not accessible when Json.NET is reading the contained class.
- 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屋!