在ContractResolver中如何设置JsonProperty反序列化必填、序列化非必填?
实现JsonProperty仅在反序列化时必填、序列化时无需校验
如果你在ContractResolver里给JsonProperty设置了Required = Required.Always,但只想让这个必填规则在反序列化时生效(序列化时允许属性为null,不抛出异常),可以通过自定义ContractResolver结合重写JsonProperty的序列化逻辑来实现。
核心思路
Json.NET的Required属性默认会同时作用于序列化和反序列化:序列化时如果属性为null且Required=Always,会抛出JsonSerializationException;反序列化时如果缺少属性或属性为null,也会抛出异常。我们需要让序列化时跳过这个必填校验,只保留反序列化时的校验逻辑。
具体做法是:
- 自定义ContractResolver,重写
CreateProperty方法。 - 对需要特殊处理的属性(比如标记了
[JsonRequired]或已设置Required=Always的属性),替换为自定义的JsonProperty子类。 - 在自定义
JsonProperty的WriteProperty方法中,临时将Required改为Required.Default,跳过序列化时的null校验,执行完序列化后再恢复原值。
完整代码实现
using System; using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; public class RequiredOnlyOnDeserializationContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); // 匹配两种场景:一是属性标记了[JsonRequired],二是你主动设置了Required=Always if (property.Required == Required.Always || member.GetCustomAttribute<JsonRequiredAttribute>() != null) { property = new RequiredOnlyOnDeserializationJsonProperty(property); } return property; } // 自定义JsonProperty,重写序列化逻辑跳过必填校验 private class RequiredOnlyOnDeserializationJsonProperty : JsonProperty { private readonly Required _originalRequired; public RequiredOnlyOnDeserializationJsonProperty(JsonProperty original) { // 复制原始JsonProperty的所有公共属性 var props = typeof(JsonProperty).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in props) { prop.SetValue(this, prop.GetValue(original)); } _originalRequired = original.Required; } public override void WriteProperty(JsonWriter writer, object value, JsonSerializer serializer) { // 序列化时临时取消必填校验 var tempRequired = Required; Required = Required.Default; base.WriteProperty(writer, value, serializer); // 恢复原有的必填设置 Required = tempRequired; } } }
测试验证(对应你的测试用例)
先定义测试类:
public class B { [JsonRequired] public string Property { get; set; } }
使用自定义ContractResolver进行测试:
using Shouldly; using Xunit; public class SomeFineTest { private static readonly JsonSerializerSettings _settings = new() { ContractResolver = new RequiredOnlyOnDeserializationContractResolver() }; [Fact] public void Test1_序列化时属性为Null不报错() => Should.NotThrow(() => JsonConvert.SerializeObject(new B { Property = null }, _settings)); [Fact] public void Test2_反序列化时属性为Null抛出异常() => Should.Throw<JsonSerializationException>(() => JsonConvert.DeserializeObject<B>("{ Property: null }", _settings)); [Fact] public void Test3_反序列化时缺少属性抛出异常() => Should.Throw<JsonSerializationException>(() => JsonConvert.DeserializeObject<B>("{ }", _settings)); }
说明
- 自定义ContractResolver会自动识别标记了
[JsonRequired]的属性,或者你在ContractResolver中主动设置Required=Always的属性。 - 序列化时通过临时修改
Required属性,跳过null值校验,保证即使属性为null也能正常序列化。 - 反序列化时保留原有的
Required.Always规则,确保缺少属性或属性为null时抛出异常。
内容的提问来源于stack exchange,提问作者asgerhallas
相关产品推荐
相关产品推荐

