You need to enable JavaScript to run this app. 使用自定义JsonConverter反序列化接口为具体子类型时出现栈溢出
优惠活动
大模型
产品
解决方案
定价
更多

使用自定义JsonConverter反序列化接口为具体子类型时出现栈溢出

栈溢出根因定位
  • 递归触发的核心逻辑链条:你在IBase接口上标记了自定义转换器MyJsonConverter,Newtonsoft.Json的路由规则是,只要某个类型实现了带转换器标记的接口,反序列化该类型时就会自动触发接口上绑定的转换器。
  • 你在MyJsonConverter.ReadJson内部调用JObject.ToObject<DerivedA>()时,由于DerivedA实现了IBase接口,序列化器会再次路由到MyJsonConverter.ReadJson方法,形成无限递归,最终触发栈溢出。
  • 现有代码的其他可运行性问题:
    • MyJsonConverter没有继承JsonConverter基类,也没有实现必须的抽象成员,属于不合法的转换器实现,无法正常被序列化器加载
    • 分支判断没有实际读取JSON中的type字段值,同时存在大小写不匹配的问题(JSON传入的是小写a/b,判断逻辑对比的是大写A/B)
    • 派生类中的属性是默认私有访问级别,序列化器无法正常读写,需要显式标记为public才能被正确反序列化
可落地的规避方案

所有方案的核心都是切断“反序列化派生类时再次触发IBase上自定义转换器”的递归路径,常见实现有三种:

方案1:临时构造不带自定义转换器的序列化配置

这是对现有代码改动最小的实现,在ReadJson内部做派生类反序列化时,手动过滤掉当前的自定义转换器,避免递归触发。
参考实现:

public class MyJsonConverter : JsonConverter
{
    // 只对IBase类型启用转换逻辑
    public override bool CanConvert(Type objectType) => typeof(IBase).IsAssignableFrom(objectType);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jsonObject = JObject.Load(reader);
        var typeValue = jsonObject["type"]?.ToString().ToLower();

        // 构造过滤了当前转换器的序列化配置,打破递归链条
        var tempSettings = new JsonSerializerSettings
        {
            Converters = serializer.Converters.Where(c => c.GetType() != typeof(MyJsonConverter)).ToList()
        };
        var tempSerializer = JsonSerializer.Create(tempSettings);

        return typeValue switch
        {
            "a" => jsonObject.ToObject<DerivedA>(tempSerializer),
            "b" => jsonObject.ToObject<DerivedB>(tempSerializer),
            _ => throw new ArgumentException($"无法识别的type标识: {typeValue}")
        };
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
        => throw new NotImplementedException();

    public override bool CanWrite => false;
}

方案2:调整转换器的匹配范围

把CanConvert的匹配规则改成只匹配IBase接口本身,不匹配实现它的派生类,这样反序列化DerivedA/DerivedB这类具体派生类时,转换器根本不会被触发,自然不存在递归。
只需要调整CanConvert方法的实现即可:

// 仅当反序列化的目标类型是IBase接口本身时,才启用自定义转换
public override bool CanConvert(Type objectType) => objectType == typeof(IBase);

这种方式不需要临时构造序列化配置,直接用传入的原始serializer做派生类反序列化即可,逻辑更简洁。

方案3:手动实例化派生类后填充属性

在判断完要使用的派生类后,直接手动实例化对象,再通过序列化器的Populate方法把JSON内容填充到实例上,全程不触发对IBase接口的转换器路由。
参考实现:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    JObject jsonObject = JObject.Load(reader);
    var typeValue = jsonObject["type"]?.ToString().ToLower();

    IBase target = typeValue switch
    {
        "a" => new DerivedA(),
        "b" => new DerivedB(),
        _ => throw new ArgumentException($"无法识别的type标识: {typeValue}")
    };

    // 基于JObject创建独立Reader,对已实例化的具体类做属性填充,不会触发递归
    using var subReader = jsonObject.CreateReader();
    serializer.Populate(subReader, target);
    return target;
}

内容的提问来源于stack exchange,提问作者ChinPaagn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.09 12:45:03