.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
相关产品推荐
相关产品推荐

