具有多态子对象的Json.Net序列化

我们希望能够从/到C#类的序列化/反序列化JSON,主类有一个多态的子对象的实例。 这样做很容易使用Json.Net的TypeNameHandling.Auto设置。 不过,我们希望没有“$ type”字段。

第一个想法是能够将“$ type”重命名为我们select的值,并将该types的值设置为能够正确映射子types的枚举。 我没有看到这是一个select,但会很高兴听到是否有可能。

第二个想法是沿着以下几行…下面是类的第一遍,顶级类有一个指示器(SubTypeType)的子对象(SubTypeData)中包含的数据types。 我已经钻了一些Json.Net文档,并尝试了一些东西,但没有运气。

我们现在可以完全控制数据定义,但是一旦部署完成,事情就会被locking。

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

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

  1. 当Json.NET正在读取包含的类时,容器类实例是不可访问的。
  2. 如果您以后需要将SubTypeClassBase属性转换为列表,则无法放置子types信息。

相反,我会build议添加子types信息作为基类中的一个属性:

 [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的对象被序列化,自定义子types枚举就会被序列化。 完成之后,对于反序列化,您可以创build一个JsonConverter ,将给定的SubTypeClassBase的json加载到临时的JObject ,检查"Type"属性的值,并将JSON对象反序列化为相应的类。

下面的原型实现:

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

您可以尝试使用JsonSubtypes转换器实现,该实现支持使用枚举值注册types映射。

在你的情况下,它看起来像这样:

  public class MainClass { public SubTypeClassBase SubTypeData { get; set; } } [JsonConverter(typeof(JsonSubtypes), "SubTypeType")] [JsonSubtypes.KnownSubType(typeof(SubTypeClass1), SubType.WithAaaField)] [JsonSubtypes.KnownSubType(typeof(SubTypeClass2), SubType.WithZzzField)] public class SubTypeClassBase { public SubType SubTypeType { get; set; } } public class SubTypeClass1 : SubTypeClassBase { public string AaaField { get; set; } } public class SubTypeClass2 : SubTypeClassBase { public string ZzzField { get; set; } } public enum SubType { WithAaaField, WithZzzField } [TestMethod] public void Deserialize() { var obj = JsonConvert.DeserializeObject<MainClass>("{\"SubTypeData\":{\"ZzzField\":\"zzz\",\"SubTypeType\":1}}"); Assert.AreEqual("zzz", (obj.SubTypeData as SubTypeClass2)?.ZzzField); }