You need to enable JavaScript to run this app. JSON.NET如何实现同名称属性旧类型反序列化与新类型序列化?
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.08 08:45:00