Newtonsoft.Json自定义转换器反序列化动态类型丢失$type信息的解决方法
问题描述
现有集合:
List<IInterface> list;
这个集合已经用TypeNameHandling.Auto序列化,JSON里包含$type动态类型元信息。默认情况下Newtonsoft.Json能通过$type识别具体派生类,自动调用对应构造函数完成反序列化,这一步没问题。
现在想自定义对象创建逻辑(比如用自己的MakeObject方法实例化),尝试继承CustomCreationConverter<IInterface>并重写Create方法:
public override IInterface Create(Type objectType)
但此时objectType永远是IInterface,不是实际的派生实现类,$type的元信息没被传进来,根本没法创建正确的对象。
无效尝试代码
public class CustomConverter : CustomCreationConverter<Example.IInterface> { public override Example.IInterface Create(Type objectType) { return Example.MakeObject(objectType); // 没用,objectType始终是IInterface } } public class Example { public interface IInterface { }; public class A : IInterface { public int content; }; public class B : IInterface { public float data; }; public static IInterface MakeObject(Type t) { if (t == typeof(IInterface)) { throw new Exception(); } return t == typeof(A) ? new A() : new B(); } public static void Serialize() { var settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }; JsonSerializer serializer = JsonSerializer.Create(settings); // serializer.Converters.Add(new CustomConverter()); // 不能同时用,二选一 List<IInterface> list = new() { MakeObject(typeof(A)), MakeObject(typeof(B)) }; using (StreamWriter sw = new("example.json")) { serializer.Serialize(sw, list); } // 需要用MakeObject把example.json反序列化为List<IInterface> // 用CustomConverter无效 using (JsonTextReader rd = new JsonTextReader(new StreamReader("example.json"))) { List<IInterface> list2 = serializer.Deserialize<List<IInterface>>(rd); } } }
解决方案:用自定义JsonConverter替代CustomCreationConverter
CustomCreationConverter的设计缺陷就是会跳过$type的解析,直接用声明的接口类型调用Create方法。要保留$type识别逻辑,同时自定义对象创建,得直接继承JsonConverter实现完整流程:
public class InterfaceConverter : JsonConverter { public override bool CanConvert(Type objectType) { // 只处理IInterface及其派生类 return typeof(Example.IInterface).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 先把整个JSON对象加载到JObject,方便读取$type var jObject = JObject.Load(reader); // 读取$type字段,拿到实际的派生类型 var typeName = jObject["$type"].Value<string>(); var actualType = Type.GetType(typeName); if (actualType == null) throw new InvalidOperationException($"找不到类型: {typeName}"); // 用自定义的MakeObject创建实例 var instance = Example.MakeObject(actualType); // 让序列化器自动把JSON数据填充到实例里 serializer.Populate(jObject.CreateReader(), instance); return instance; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 序列化用默认逻辑,保留$type信息 serializer.Serialize(writer, value); } }
使用示例
修改Serialize方法的配置,加入自定义转换器:
public static void Serialize() { var settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto, Converters = new List<JsonConverter> { new InterfaceConverter() } }; JsonSerializer serializer = JsonSerializer.Create(settings); List<Example.IInterface> list = new() { Example.MakeObject(typeof(Example.A)), Example.MakeObject(typeof(Example.B)) }; // 序列化 using (StreamWriter sw = new("example.json")) { serializer.Serialize(sw, list); } // 反序列化 using (JsonTextReader rd = new JsonTextReader(new StreamReader("example.json"))) { List<Example.IInterface> list2 = serializer.Deserialize<List<Example.IInterface>>(rd); // 现在list2里的元素是正确的A、B实例,属性也会被正确填充 } }
方案说明
- 直接继承
JsonConverter能完全掌控反序列化流程:先读$type拿实际类型,再调用自定义创建逻辑。 - 用
JObject.Load加载整个JSON,既可以读取$type,还能通过Populate自动填充对象属性,不用手动解析每个字段。 - 序列化复用默认逻辑,保证
$type信息正常写入。
内容的提问来源于stack exchange,提问作者FilippoL
相关产品推荐
相关产品推荐

