c# - 如何防止 Json.NET 对缺少的构造函数参数使用默认值,同时仍然对属性使用默认值?

标签 c# .net json.net

<分区>

有没有办法告诉 JSON.net,当它尝试使用构造函数反序列化时(如果没有默认构造函数),它不应该为构造函数参数分配默认值,并且它应该只调用构造函数,如果每个构造函数参数在 JSON 字符串中表示?同样的序列化程序在调用属性/字段 setter 时应该使用默认值,该规则仅适用于构造函数。这里的枚举值似乎都不合适:http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_DefaultValueHandling.htm

解决方案不应依赖于将任何属性应用于被反序列化的类型。

例如,通过将狗的年龄设置为 0(int 的默认值),json 字符串 "{}" 将反序列化为 Dog 类型的对象.我想要一个通用的、不基于属性的解决方案来防止这种情况发生。在这种情况下,{"age":4} 会起作用,因为 age 在 JSON 字符串中指定并且对应于构造函数参数。

public class Dog
{
    public Dog(int age)
    {
        this.Age = age;
    }

    public int Age { get; }
}

但是,如果这样指定 Dog,则 "{}" 应该反序列化为 Age == 0 的 Dog,因为Dog 不是使用构造函数创建的。

public class Dog
{   
    public int Age { get; set; }
}

至于“你为什么要这样做”......具有构造函数的对象通常在性质上不同于 POCO,因为它涉及到它们的属性。使用构造函数来存储属性值而不是 POCO 上的可设置属性通常意味着您想要验证/约束属性值。因此,在存在构造函数的情况下不允许使用默认值进行反序列化是合理的。

最佳答案

当 Json.NET 遇到没有无参数构造函数但有参数化构造函数的对象时,它将调用该构造函数来创建对象,matching the JSON property names to the constructor arguments by name using reflection通过不区分大小写的最佳匹配算法。 IE。名称也出现在构造函数中的属性将通过构造函数调用设置,而不是 set 方法(即使有)。

因此,您可以通过将等效属性标记为 required 来根据需要标记构造函数参数。 :

public class Dog
{
    public Dog(int age)
    {
        this.Age = age;
    }

    [JsonProperty(Required = Required.Always)]
    public int Age { get; }
}

现在JsonConvert.DeserializeObject<Dog>(jsonString)将在 "age" 时抛出属性丢失。

因为这是您总是想要的东西,您可以创建一个 custom contract resolver继承自 DefaultContractResolver CamelCasePropertyNamesContractResolver 根据需要自动标记传递给构造函数的属性,而不需要属性:

public class ConstructorParametersRequiredContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
    {
        var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);

        if (property != null && matchingMemberProperty != null)
        {
            var required = matchingMemberProperty.Required;
            // If the member is already explicitly marked with some Required attribute, don't override it.
            // In Json.NET 12.0.2 and later you can use matchingMemberProperty.IsRequiredSpecified to check to see if Required is explicitly specified.
            // if (!matchingMemberProperty.IsRequiredSpecified) 
            if (required == Required.Default)
            {
                if (matchingMemberProperty.PropertyType != null && (matchingMemberProperty.PropertyType.IsValueType && Nullable.GetUnderlyingType(matchingMemberProperty.PropertyType) == null))
                {
                    required = Required.Always;
                }
                else
                {
                    required = Required.AllowNull;
                }
                // It turns out to be necessary to mark the original matchingMemberProperty as required.
                property.Required = matchingMemberProperty.Required = required;
            }
        }

        return property;
    }
}

然后构造一个解析器的实例:

static IContractResolver requiredResolver = new ConstructorParametersRequiredContractResolver();

并按如下方式使用它:

var settings = new JsonSerializerSettings { ContractResolver = requiredResolver };
JsonConvert.DeserializeObject<T>(jsonString, settings)

现在反序列化将抛出 "age" JSON 中缺少属性。

注意事项:

演示 fiddle here .

关于c# - 如何防止 Json.NET 对缺少的构造函数参数使用默认值,同时仍然对属性使用默认值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37416233/

相关文章:

C# XML 序列化排除写类名

.net - Ubuntu - 安装 .net 核心

c# - JsonConverter 不适用于模型绑定(bind)

c# - JSON.net(反)序列化未类型化的属性

c# - Entity Framework : Get Inherited Types directly through Context

c# - 为什么 .Equals 在此 LINQ 示例中不起作用?

c# - 自动更新 : Is this secure?

C# 派生类不会覆盖基方法

c# - try { return x; 中到底发生了什么? } 最后 { x = null; } 陈述?

C# Json.NET WCF Rest 日期时间格式