You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:10:34