Using custom JsonConverter and TypeNameHandling in Json.net

前端 未结 2 1758
没有蜡笔的小新
没有蜡笔的小新 2020-12-04 01:15

I have a class with an interface-typed property like:

public class Foo
{
    public IBar Bar { get; set; }
}

I also have multiple concrete

2条回答
  •  执笔经年
    2020-12-04 02:16

    Using information from Alesandr Ivanov's answer above, I created a generic WrappedJsonConverter class that wraps (and unwraps) concrete classes requiring a converter using a $wrappedType metadata property that follows the same type name serialization as the standard $type.

    The WrappedJsonConverter is added as a converter to the Interface (ie. IBar), but otherwise this wrapper is completely transparent to classes that do not require a converter and also requires no changes to the wrapped converters.

    I used a slightly different hack to get around the converter/serializer looping (static fields), but it does not require any knowledge of the serializer settings being used, and allows for the IBar object graph to have child IBar properties.

    For wrapped objects the Json looks like:

    "IBarProperty" : {
        "$wrappedType" : "Namespace.ConcreteBar, Namespace",
        "$wrappedValue" : {
            "ConvertedID" : 90,
            "ConvertedPropID" : 70
            ...
        }
    }
    

    The full gist can be found here.

    public class WrappedJsonConverter : JsonConverter where T : class
    {        
        [ThreadStatic]
        private static bool _canWrite = true;
        [ThreadStatic]
        private static bool _canRead = true;
    
        public override bool CanWrite
        {
            get
            {
                if (_canWrite)
                    return true;
    
                _canWrite = true;
                return false;
            }
        }
    
        public override bool CanRead
        {
            get
            {
                if (_canRead)
                    return true;
    
                _canRead = true;
                return false;
            }
        }
    
        public override T ReadJson(JsonReader reader, T existingValue, JsonSerializer serializer)
        {
            var jsonObject = JObject.Load(reader);
            JToken token;
            T value;
    
            if (!jsonObject.TryGetValue("$wrappedType", out token))
            {
                //The static _canRead is a terrible hack to get around the serialization loop...
                _canRead = false;
                value = jsonObject.ToObject(serializer);
                _canRead = true;
                return value;
            }
    
            var typeName = jsonObject.GetValue("$wrappedType").Value();
    
            var type = JsonExtensions.GetTypeFromJsonTypeName(typeName, serializer.Binder);
    
            var converter = serializer.Converters.FirstOrDefault(c => c.CanConvert(type) && c.CanRead);
    
            var wrappedObjectReader = jsonObject.GetValue("$wrappedValue").CreateReader();
    
            wrappedObjectReader.Read();
    
            if (converter == null)
            {
                _canRead = false;
                value = (T)serializer.Deserialize(wrappedObjectReader, type);
                _canRead = true;
            }
            else
            {
                value = (T)converter.ReadJson(wrappedObjectReader, type, existingValue, serializer);
            }
    
            return value;
        }
    
        public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
        {
            var type = value.GetType();
            var converter = serializer.Converters.FirstOrDefault(c => c.CanConvert(type) && c.CanWrite);
    
            if (converter == null)
            {
                //This is a terrible hack to get around the serialization loop...
                _canWrite = false;
                serializer.Serialize(writer, value, type);
                _canWrite = true;
                return;
            }
    
            writer.WriteStartObject();
            {
                writer.WritePropertyName("$wrappedType");
                writer.WriteValue(type.GetJsonSimpleTypeName());
                writer.WritePropertyName("$wrappedValue");
    
                converter.WriteJson(writer, value, serializer);
            }
            writer.WriteEndObject();
        }
    }
    

提交回复
热议问题