我正在使用JSON.NET反序列化JSON(我无法控制),它代表内部Event
的特定类型的data
:
{
"id": "abc",
"type": "a",
"data": {
// Data specific to "type"="a".
"a": 1
}
}
和
{
"id": "def",
"type": "b",
"data": {
// Data specific to "type"="b".
"b": 1
}
}
应将此JSON反序列化为以下类:
public class Event
{
public string Id { get; }
public string Type { get; }
public EventDataBase Data { get; }
public Event(string id, string type, EventDataBase data)
{
this.Id = id;
this.Type = type;
this.Data = data;
}
}
public abstract class EventDataBase
{
}
public class AEventData : EventDataBase
{
public string A { get; }
public AEventData(string a)
{
this.A = a;
}
}
public class BEventData : EventDataBase
{
public string B { get; }
public BEventData(string b)
{
this.B = b;
}
}
在反序列化EventDataBase
时,应该实例化正确的继承类Event
。
此问题有很多解决方案,但它们通常涉及type
属于data
,而不是父项。如果它确实属于父级,the answer通常会将Event
的JSON转换为JObject
,然后手动将其反序列化。虽然这是可能的,但仅仅利用JSON.NET的 part 进行反序列化感觉就像是一种黑客攻击。
我提出的解决方案使用IReferenceResolver
添加对type
的引用,然后在尝试反序列化data
时,获取对父类型的引用,以及用它来确定需要反序列化的具体类:
public class CustomContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (member.DeclaringType == typeof(Event) && member.Name == nameof(Event.Type))
{
property.Order = Int32.MinValue;
property.MemberConverter = new EventTypeConverter();
}
else if (member.DeclaringType == typeof(Event) && member.Name == nameof(Event.Data))
{
property.Order = Int32.MaxValue;
property.MemberConverter = new EventDataConverter();
}
return property;
}
private class EventTypeConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var eventType = reader.Value as string;
serializer.ReferenceResolver.AddReference(serializer, "parentEvent.type", eventType);
return eventType;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
private class EventDataConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(EventDataBase);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var eventType = serializer.ReferenceResolver.ResolveReference(serializer, "parentEvent.type") as string;
var eventDataType = GetEventDataTypeFromEventType(eventType);
return serializer.Deserialize(reader, eventDataType);
}
private static Type GetEventDataTypeFromEventType(string eventType)
{
switch (eventType?.ToLower())
{
case "a":
return typeof(AEventData);
case "b":
return typeof(BEventData);
default:
throw new InvalidOperationException();
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
Here's a .NET Fiddle此示例的实例。
这是IReferenceResolver
的有效使用吗? JSON.NET是否在每次调用反序列化期间清除引用解析器(以便多次调用不存储旧引用)?它是线程安全的吗?