You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET中读取JSON的[Obsolete]属性但不序列化写入的实现方案

针对Newtonsoft.Json和System.Text.Json的读写配置方案

核心需求

  • 反序列化:读取包含[Obsolete]属性的旧版JSON数据,所有模型属性(包括标记[Obsolete]的)均需支持读取。
  • 序列化:仅写入带有[Required]属性的字段,其他属性(含[Obsolete])不输出。

问题1:Newtonsoft.Json既不读取也不写入[Obsolete]属性的原因及修复

你的ExcludeObsoletePropertiesResolver通过GetSerializableMembers移除了所有标记[Obsolete]的成员,导致这些成员完全从JSON契约中消失——反序列化时无法匹配JSON字段,序列化时也不会输出。这是逻辑错误,因为你需要区分读写行为:反序列化保留所有成员,序列化仅保留[Required]成员。

修复方案

重写DefaultContractResolver的CreateProperty方法,替代GetSerializableMembers:

  • 反序列化时允许读取所有属性(不从契约中移除)。
  • 序列化时仅保留带有[Required]属性的字段。
public class RequiredOnlySerializationResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        
        // 序列化仅保留[Required]字段
        property.ShouldSerialize = instance => 
            member.IsDefined(typeof(RequiredAttribute), true);
        
        // 反序列化允许读取所有属性
        property.ShouldDeserialize = instance => true;
        
        return property;
    }
}

使用该Resolver后,反序列化会读取所有字段(包括Obsolete),序列化仅输出Required字段。


问题2:System.Text.Json当前方案是否最优

你当前的ExcludeObsoleteProperties仅排除了[Obsolete]属性的序列化,但未严格实现"仅写入[Required]字段"的需求(若存在既无[Required]也无[Obsolete]的属性,会被意外序列化)。

优化方案

调整修改器,序列化时仅保留[Required]字段,同时确保反序列化能读取所有属性:

public static class JsonExtensions
{
    public static void IncludeOnlyRequiredProperties(JsonTypeInfo typeInfo)
    {
        if (typeInfo.Kind != JsonTypeInfoKind.Object)
            return;
            
        foreach (var property in typeInfo.Properties)
        {
            // 序列化仅保留[Required]字段
            property.ShouldSerialize = (obj, value) => 
                property.AttributeProvider?.IsDefined(typeof(RequiredAttribute), true) == true;
        }
    }
}

该实现贴合核心需求,属于System.Text.Json原生扩展方式,无需自定义转换器,是最优解之一。


问题3:是否可检测读写状态调整行为,或需分别配置

两个库均支持在单一配置中区分读写行为,但若需完全独立的读写规则,需分别配置:

Newtonsoft.Json

  • 单一配置:通过CreateProperty中的ShouldSerialize和ShouldDeserialize可分别控制读写逻辑,无需分开配置。
  • 独立配置:若读写规则差异极大,可创建两个JsonSerializerSettings实例,分别绑定不同的ContractResolver(一个用于反序列化,一个用于序列化)。

System.Text.Json

  • 单一配置:JsonTypeInfo的ShouldSerialize仅影响序列化,反序列化默认处理所有模型属性,因此单一配置即可满足需求。
  • 独立配置:若需不同反序列化规则(如忽略特定字段),可创建两个JsonSerializerOptions实例:一个带修改器用于序列化,一个不带用于反序列化。

完整测试代码

using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

// Newtonsoft.Json 配置
var newtonsoftSettings = new JsonSerializerSettings()
{
    Formatting = Formatting.Indented,
    StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
    NullValueHandling = NullValueHandling.Ignore,
    ObjectCreationHandling = ObjectCreationHandling.Reuse,
    ContractResolver = new RequiredOnlySerializationResolver()
};

// System.Text.Json 配置
var textJsonOptions = new JsonSerializerOptions()
{
    WriteIndented = true,
    Encoder = null,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate,
    TypeInfoResolver = new DefaultJsonTypeInfoResolver()
        .WithAddedModifier(JsonExtensions.IncludeOnlyRequiredProperties),
    ReadCommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true
};

// 测试JSON
var jsonString = "{\"Required\":\"RequiredValue\",\"Obsolete\":\"ObsoleteValue\",\"Extra\":\"ExtraValue\"}";

// Newtonsoft.Json 测试
var schemaNewton = JsonConvert.DeserializeObject<Schema>(jsonString, newtonsoftSettings);
var jsonNewton = JsonConvert.SerializeObject(schemaNewton, newtonsoftSettings);
// 输出仅包含Required字段:{"Required":"RequiredValue"}

// System.Text.Json 测试
var schemaText = JsonSerializer.Deserialize<Schema>(jsonString, textJsonOptions);
var jsonText = JsonSerializer.Serialize(schemaText, textJsonOptions);
// 输出仅包含Required字段:{"Required":"RequiredValue"}

public record Schema
{
    [Required]
    public string Required { get; set; } = "";

    [Obsolete]
    public string Obsolete { get; set; } = "";

    // 无属性的字段,序列化时会被排除
    public string Extra { get; set; } = "";
}

// Newtonsoft.Json 自定义Resolver
public class RequiredOnlySerializationResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = instance => member.IsDefined(typeof(RequiredAttribute), true);
        property.ShouldDeserialize = instance => true;
        return property;
    }
}

// System.Text.Json 扩展方法
public static class JsonExtensions
{
    public static void IncludeOnlyRequiredProperties(JsonTypeInfo typeInfo)
    {
        if (typeInfo.Kind != JsonTypeInfoKind.Object)
            return;
        foreach (var property in typeInfo.Properties)
        {
            property.ShouldSerialize = (obj, value) => 
                property.AttributeProvider?.IsDefined(typeof(RequiredAttribute), true) == true;
        }
    }
}

内容的提问来源于stack exchange,提问作者PieterV

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:34:57