在JsonConverter中正确使用IReferenceResolver吗?

时间:2017-05-10 13:39:08

标签: c# .net json serialization json.net

我正在使用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是否在每次调用反序列化期间清除引用解析器(以便多次调用不存储旧引用)?它是线程安全的吗?

0 个答案:

没有答案