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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:20:22