在Json.net中使用自定义JsonConverter和TypeNameHandling

我有一个类接口类型的类,如:

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

我还有IBar接口的多个具体实现,可以在运行时设置。 其中一些具体类需要自定义JsonConverter进行序列化和反序列化。

利用TypeNameHandling.Auto选项,可以完美地序列化和反序列化需要IBar类的非转换器。 另一方面,自定义序列化类没有$type name输出,虽然它们按预期序列化,但它们不能反序列化为它们的具体类型。

我试图在自定义JsonConverter自己写出$type name元数据; 但是,在反序列化时,转换器将被完全绕过。

是否有解决方法或处理此类情况的正确方法?

我解决了类似的问题,我找到了解决方案。 它不是很优雅,我认为应该有更好的方法,但至少它有效。 所以我的想法是为每个实现IBar类型使用IBar ,为IBar本身使用一个转换器。

那么让我们从模型开始:

 public interface IBar { } public class BarA : IBar { } public class Foo { public IBar Bar { get; set; } } 

现在让我们为IBar创建转换器。 它仅在反序列化JSON时使用。 它将尝试读取$type变量并调用转换器来实现类型:

 public class BarConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObj = JObject.Load(reader); var type = jObj.Value("$type"); if (type == GetTypeString()) { return new BarAJsonConverter().ReadJson(reader, objectType, jObj, serializer); } // Other implementations if IBar throw new NotSupportedException(); } public override bool CanConvert(Type objectType) { return objectType == typeof (IBar); } public override bool CanWrite { get { return false; } } private string GetTypeString() { var typeOfT = typeof (T); return string.Format("{0}, {1}", typeOfT.FullName, typeOfT.Assembly.GetName().Name); } } 

这是BarA类的转换器:

 public class BarAJsonConverter : BarBaseJsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // '$type' property will be added because used serializer has TypeNameHandling = TypeNameHandling.Objects GetSerializer().Serialize(writer, value); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var existingJObj = existingValue as JObject; if (existingJObj != null) { return existingJObj.ToObject(GetSerializer()); } throw new NotImplementedException(); } public override bool CanConvert(Type objectType) { return objectType == typeof(BarA); } } 

您可能会注意到它inheritance自BarBaseJsonConverter类,而不是JsonConverter 。 而且我们也不在WriteJsonReadJson方法中使用serializer参数。 在自定义转换器中使用serializer参数时出现问题。 你可以在这里阅读更多。 我们需要创建JsonSerializer新实例,基类是一个很好的候选者:

 public abstract class BarBaseJsonConverter : JsonConverter { public JsonSerializer GetSerializer() { var serializerSettings = JsonHelper.DefaultSerializerSettings; serializerSettings.TypeNameHandling = TypeNameHandling.Objects; var converters = serializerSettings.Converters != null ? serializerSettings.Converters.ToList() : new List(); var thisConverter = converters.FirstOrDefault(x => x.GetType() == GetType()); if (thisConverter != null) { converters.Remove(thisConverter); } serializerSettings.Converters = converters; return JsonSerializer.Create(serializerSettings); } } 

JsonHelper只是一个创建JsonSerializerSettings的类:

 public static class JsonHelper { public static JsonSerializerSettings DefaultSerializerSettings { get { return new JsonSerializerSettings { Converters = new JsonConverter[] { new BarConverter(), new BarAJsonConverter() } }; } } } 

现在它可以工作,您仍然可以使用自定义转换器进行序列化和反序列化:

 var obj = new Foo { Bar = new BarA() }; var json = JsonConvert.SerializeObject(obj, JsonHelper.DefaultSerializerSettings); var dObj = JsonConvert.DeserializeObject(json, JsonHelper.DefaultSerializerSettings); 

使用Alesandr Ivanov上面回答的信息,我创建了一个通用的WrappedJsonConverter类,它包含(和解包)具体类,需要使用$wrappedType元数据属性的转换器,该属性遵循与标准$type相同的类型名称序列化。

WrappedJsonConverter作为转换器添加到接口(即IBar ),但是否则此包装对于不需要转换器的类完全透明,并且不需要更改包装的转换器。

我使用了稍微不同的hack来绕过转换器/串行器循环(静态字段),但它不需要知道所使用的序列化器设置,并允许IBar对象图具有子IBar属性。

对于包裹的物体,Json看起来像:

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

完整的要点可以在这里找到。

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