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

