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

使用System.Text.Json序列化时出现StackOverflowException如何解决?

解决System.Text.Json多态序列化中的StackOverflowException问题

问题原因

你的代码在Write阶段抛出StackOverflowException,本质是无限递归调用:

  • 当调用JsonSerializer.Serialize(writer, value, value.GetType(), options)时,传入的options包含了当前注册的PolymorphicJsonConverter<ClassC>
  • 转换器的CanConvert方法会判断typeof(ClassC).IsAssignableFrom(value.GetType())(即子类是否可赋值给基类),结果为true
  • 序列化流程会再次调用该转换器的Write方法,形成无限递归,最终耗尽栈空间导致异常
  • 顺带一提,你的Read方法也存在同样的递归隐患,只是Write先触发了崩溃

修复方案

核心思路是在转换器内部执行序列化/反序列化时,排除当前的多态转换器,避免递归调用。可以通过克隆原选项并移除当前转换器来实现:

修复后的完整代码

using System.Text.Json;
using System.Text.Json.Serialization;

public abstract class ClassC
{
    public string PropertyC { get; set; }
    public string TypeDiscriminator => this.GetType().FullName;
}

public class ClassB : ClassC
{
    public string PropertyB { get; set; }
}

public class ClassA : ClassB
{
    public string PropertyA { get; set; }
}

public class PolymorphicJsonConverter<TBase> : JsonConverter<TBase> where TBase : ClassC
{
    public override bool CanConvert(Type typeToConvert) => typeof(TBase).IsAssignableFrom(typeToConvert);

    public override TBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using (var jsonDoc = JsonDocument.ParseValue(ref reader))
        {
            var root = jsonDoc.RootElement;
            var typeDiscriminator = root.GetProperty("TypeDiscriminator").GetString();

            var type = Type.GetType(typeDiscriminator);
            if (type == null)
            {
                throw new JsonException($"类型 {typeDiscriminator} 未找到。");
            }

            // 创建排除当前转换器的新选项,避免递归调用Read
            var newOptions = new JsonSerializerOptions(options);
            newOptions.Converters.RemoveAll(c => c.GetType() == typeof(PolymorphicJsonConverter<TBase>));
            
            return (TBase)JsonSerializer.Deserialize(root.GetRawText(), type, newOptions);
        }
    }

    public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options)
    {
        // 创建排除当前转换器的新选项,避免递归调用Write
        var newOptions = new JsonSerializerOptions(options);
        newOptions.Converters.RemoveAll(c => c.GetType() == typeof(PolymorphicJsonConverter<TBase>));
        
        JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
    }
}

public class Program
{
    public static void Main()
    {
        var options = new JsonSerializerOptions
        {
            Converters = { new PolymorphicJsonConverter<ClassC>() },
            WriteIndented = true
        };

        ClassA classA = new ClassA { PropertyA = "ValueA", PropertyB = "ValueB", PropertyC = "ValueC" };

        string json = JsonSerializer.Serialize<ClassC>(classA, options);
        ClassC deserialized = JsonSerializer.Deserialize<ClassC>(json, options);

        Console.WriteLine(json);
        Console.WriteLine(deserialized is ClassA); // 应输出 'True'
    }
}

关键说明

  • 通过new JsonSerializerOptions(options)克隆原选项,保证其他配置(如缩进、其他转换器)得以保留
  • 使用RemoveAll移除当前类型的转换器,避免内部序列化/反序列化再次触发该转换器,彻底切断递归链
  • 修复后既保留了类型鉴别器的多态支持,又能正确序列化/反序列化所有子类属性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:20:06