使用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
相关产品推荐
相关产品推荐

