JSON.NET如何实现同名称属性旧类型反序列化与新类型序列化?
适配类结构迭代的Json.Net兼容实现方案
针对类属性类型整体替换的序列化兼容场景,仅通过Json.Net原生的自定义JsonConverter扩展能力即可满足需求,无需新增任何额外公开属性,具体实现如下:
核心实现思路
通用的属性名映射方案仅支持同类型属性的字段重命名适配,当属性从可空decimal基础类型整体替换为复杂自定义类时,就无法跨类型完成值映射,需要借助框架原生钩子同时兼顾两个核心目标:
- 反序列化阶段:自动识别旧格式的纯数字Fee值、新格式的Fee对象值,统一映射为新结构的
Fee类实例 - 序列化阶段:严格按照新类结构输出JSON,绝不回退输出旧的纯数字格式
具体代码实现
1. 编写兼容新旧格式的自定义转换器
public class LegacyFeeJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) { // 仅处理Fee类型的序列化/反序列化逻辑 return objectType == typeof(Fee); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 兼容null值场景 if (reader.TokenType == JsonToken.Null) return null; // 兼容旧格式:直接读取纯数字格式的Fee值 if (reader.TokenType is JsonToken.Float or JsonToken.Integer) { var legacyAmount = serializer.Deserialize<decimal?>(reader); return new Fee { Amount = legacyAmount, Type = default // 旧数据无Type字段,可根据业务规则调整默认值 }; } // 兼容新格式:按标准对象结构反序列化Fee实例 if (reader.TokenType == JsonToken.StartObject) { var fee = new Fee(); serializer.Populate(reader, fee); return fee; } throw new JsonSerializationException($"解析Fee值时遇到不支持的token类型:{reader.TokenType}"); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var fee = (Fee)value; // 序列化时统一输出新的对象结构,不回退旧格式 writer.WriteStartObject(); writer.WritePropertyName(nameof(Fee.Amount)); serializer.Serialize(writer, fee.Amount); writer.WritePropertyName(nameof(Fee.Type)); serializer.Serialize(writer, fee.Type); writer.WriteEndObject(); } // 开启自定义序列化输出逻辑,保证新结构格式生效 public override bool CanWrite => true; }
2. 挂载转换器到目标类结构
直接通过Json.Net提供的JsonConverter特性把转换器绑定到Fee属性上,全程不新增任何额外公开属性:
public class Something { [JsonConverter(typeof(LegacyFeeJsonConverter))] public Fee Fee { get; set; } } public class Fee { public decimal? Amount { get; set; } public int Type { get; set; } }
效果验证
- 旧数据反序列化:原本存储在数据库中的旧格式JSON
{"Fee": 199.99},可以直接反序列化为新结构的Something实例,自动把199.99映射到Fee.Amount属性,Type取默认值 - 新数据序列化:新结构的
Something实例(比如Fee.Amount=199.99、Fee.Type=1)序列化后,会输出符合新结构要求的JSON:{"Fee":{"Amount":199.99,"Type":1}},不会保留旧的纯数字格式
方案合规说明
- 完全符合开发约束:类的公开接口没有新增类似
NewFee的冗余属性,所有兼容逻辑都封装在转换器内部 - 无全局侵入:转换器仅作用于挂载的Fee属性,不会影响其他类的Json.Net序列化行为
- 支持后续扩展:如果Fee类后续再迭代结构,只需要在这个自定义转换器里补充对应格式的识别逻辑即可
内容的提问来源于stack exchange,提问作者Esben Bach
相关产品推荐
相关产品推荐

