基于JsonConverter<T>实现JSON版本迁移器遇递归及实例化问题
解决Json.NET JsonConverter迁移时的无限递归与无参构造问题
核心思路
你的迁移逻辑方向是对的:先修正JSON结构,再反序列化到目标类型。当前遇到的两个问题可以通过以下方式解决:
- 直接调用传入的
serializer.Deserialize会触发当前转换器,导致无限递归——需要临时禁用当前转换器来绕开递归 - 目标类型无无参构造,不想用反射——利用Json.NET原生的契约解析能力来处理实例创建,无需手动反射
解决无限递归:临时排除当前转换器
在JsonMigrator<T>的ReadJson方法中,不要直接使用传入的serializer进行反序列化,而是创建一个移除了当前转换器的新JsonSerializer实例,这样反序列化时就不会再次触发当前转换器,避免递归。
复用Json.NET原生实例创建能力
Json.NET本身可以通过ContractResolver解析目标类型的构造逻辑(哪怕没有无参构造,比如私有构造、带参数的构造),只要用排除当前转换器的新Serializer来反序列化,就能让Json.NET自动处理实例创建,不需要手动调用Activator.CreateInstance。
代码实现示例
抽象迁移转换器基类
public abstract class JsonMigrator<T> : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 1. 读取原始JSON到JObject,方便修改结构 var jObject = JObject.Load(reader); // 2. 调用抽象方法执行具体的JSON迁移逻辑(子类实现) MigrateJson(jObject); // 3. 创建排除当前转换器的新Serializer,避免递归 var newSerializer = new JsonSerializer(); // 复制原Serializer的配置,保证行为一致 newSerializer.ContractResolver = serializer.ContractResolver; newSerializer.DateFormatHandling = serializer.DateFormatHandling; newSerializer.NullValueHandling = serializer.NullValueHandling; // 只添加除当前转换器外的其他转换器 newSerializer.Converters.AddRange(serializer.Converters.Where(c => c.GetType() != this.GetType())); // 4. 让Json.NET自动处理实例创建并反序列化 return jObject.ToObject<T>(newSerializer); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 序列化无需迁移的话,直接用默认逻辑即可 serializer.Serialize(writer, value); } // 抽象方法:子类实现具体的JSON字段迁移、结构修改逻辑 protected abstract void MigrateJson(JObject jObject); }
子类迁移实现示例(以User类为例)
public class UserMigrator : JsonMigrator<User> { protected override void MigrateJson(JObject jObject) { // 示例1:旧版本用"UserName"字段,新版本用"Name",迁移时替换 if (jObject.ContainsKey("UserName")) { jObject["Name"] = jObject["UserName"]; jObject.Remove("UserName"); } // 示例2:给缺失的字段设置默认值 if (!jObject.ContainsKey("CreatedAt")) { jObject["CreatedAt"] = DateTime.UtcNow; } } }
使用方式
注册转换器后正常反序列化即可:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new UserMigrator()); var oldJson = "{\"UserName\":\"Alice\",\"Age\":30}"; var user = JsonConvert.DeserializeObject<User>(oldJson, settings); // 最终user.Name为"Alice",CreatedAt会被自动填充
关键细节说明
- 避免递归的核心:新创建的
JsonSerializer排除了当前转换器,所以jObject.ToObject<T>不会再次触发JsonMigrator<T>的ReadJson方法 - 配置一致性:复制原Serializer的契约解析器、日期格式等配置,保证反序列化行为和原有逻辑完全一致
- 原生实例创建:
JObject.ToObject<T>依赖Json.NET的契约解析逻辑,自动适配目标类型的构造方式,比反射更稳健,完全贴合Json.NET的原生行为
内容的提问来源于stack exchange,提问作者Smolakian
相关产品推荐
相关产品推荐

