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

.NET Framework4.8中Newtonsoft.Json实现属性条件必填的方案

问题

现有如下类定义:

public class Foo
{
    [JsonProperty]
    public int Number { get; set; }

    [JsonProperty]
    public string Text { get; set; }
}

需要为Text属性实现条件必填逻辑:当Number > 10时,JSON字符串中必须包含Text属性;当Number ≤ 10时,该属性可省略。无法直接使用[JsonProperty(Required = Required.Always)]这种非条件的必填属性配置。合法与不合法示例如下:

{ "Number": 5 } // 合法
{ "Number": 20 } // 不合法
{ "Number": 20, "Text": "Bar" } // 合法

项目基于.NET Framework v4.8,使用Newtonsoft.Json,API部署在IIS上,通过中间件完成序列化,未手动调用JsonConvert.Deserialize<T>方法,请问如何实现该条件必填校验?

解决方案

针对你的场景,有三种实用方案可以实现条件必填校验,适配.NET Framework 4.8 + Newtonsoft.Json的Web API环境:

方案一:实现IValidatableObject做模型级验证

这是最直接的方案,利用ASP.NET Web API内置的模型验证机制,无需修改Json序列化配置:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;

public class Foo : IValidatableObject
{
    [JsonProperty]
    public int Number { get; set; }

    [JsonProperty]
    public string Text { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Number > 10 && string.IsNullOrEmpty(Text))
        {
            yield return new ValidationResult(
                "当Number大于10时,Text属性为必填项",
                new[] { nameof(Text) });
        }
    }
}

Web API会自动在模型绑定完成后触发验证,若校验失败,会返回400 Bad Request响应,包含对应的错误信息。

方案二:自定义ContractResolver在反序列化阶段校验

如果需要在Json反序列化过程中就拦截非法数据(更早触发校验),可以自定义契约解析器:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Reflection;

public class ConditionalRequiredContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        // 针对Foo类的Text属性设置条件规则
        if (member.DeclaringType == typeof(Foo) && member.Name == nameof(Foo.Text))
        {
            property.Required = Required.Default;
            // 控制是否需要反序列化该属性
            property.ShouldDeserialize = instance =>
            {
                var foo = instance as Foo;
                return foo == null || foo.Number <= 10 || !string.IsNullOrEmpty(foo.Text);
            };
            // 校验赋值过程,确保Number>10时Text不为空
            property.ValueProvider = new ConditionalRequiredValueProvider(property.ValueProvider);
        }

        return property;
    }
}

public class ConditionalRequiredValueProvider : IValueProvider
{
    private readonly IValueProvider _originalProvider;

    public ConditionalRequiredValueProvider(IValueProvider originalProvider)
    {
        _originalProvider = originalProvider;
    }

    public object GetValue(object target) => _originalProvider.GetValue(target);

    public void SetValue(object target, object value)
    {
        var foo = target as Foo;
        if (foo != null && foo.Number > 10 && (value == null || string.IsNullOrWhiteSpace(value.ToString())))
        {
            throw new JsonSerializationException("当Number大于10时,Text属性不能为空");
        }
        _originalProvider.SetValue(target, value);
    }
}

然后在App_Start/WebApiConfig.cs中全局注册这个契约解析器:

using Newtonsoft.Json;
using System.Web.Http;

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // 其他路由/配置...

        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new ConditionalRequiredContractResolver();
    }
}

反序列化时若不符合规则,会抛出JsonSerializationException,可通过全局异常过滤器统一处理这类异常并返回友好响应。

方案三:自定义可复用的RequiredIfAttribute

如果需要在多个模型中复用条件必填逻辑,可以自定义一个验证特性:

using System.ComponentModel.DataAnnotations;
using System.Reflection;

public class RequiredIfAttribute : ValidationAttribute
{
    private readonly string _dependentProperty;
    private readonly int _threshold;

    public RequiredIfAttribute(string dependentProperty, int threshold)
    {
        _dependentProperty = dependentProperty;
        _threshold = threshold;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var dependentProp = validationContext.ObjectType.GetProperty(_dependentProperty);
        if (dependentProp == null)
        {
            return new ValidationResult($"依赖属性「{_dependentProperty}」不存在");
        }

        var dependentValue = dependentProp.GetValue(validationContext.ObjectInstance);
        if (dependentValue is int number && number > _threshold && (value == null || string.IsNullOrWhiteSpace(value.ToString())))
        {
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }

        return ValidationResult.Success;
    }
}

在Foo类中直接使用该特性:

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

public class Foo
{
    [JsonProperty]
    public int Number { get; set; }

    [JsonProperty]
    [RequiredIf(nameof(Number), 10, ErrorMessage = "当Number大于10时,Text为必填项")]
    public string Text { get; set; }
}

这种方式同样依赖Web API的模型验证机制,优势是代码复用性强,适合多模型场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:45:00