如何防止 Json.NET 对缺少的构造函数参数使用默认值,同时仍使用属性的默认值? [关闭]

2023-12-26

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

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

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

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

    public int Age { get; }
}

然而,如果Dog被指定为这样,那么"{}" should反序列化为 Age == 0 的 Dog,因为 Dog 不是使用构造函数创建的。

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

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


当Json.NET遇到一个没有无参数构造函数但有参数构造函数的对象时,它会调用该构造函数来创建该对象,使用反射按名称将 JSON 属性名称与构造函数参数进行匹配 http://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm通过不区分大小写的最佳匹配算法。 IE。名称也出现在构造函数中的属性将通过构造函数调用进行设置,而不是通过 set 方法(即使有)。

因此,您可以通过将等效属性标记为所需的来标记构造函数参数required http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonPropertyAttribute_Required.htm:

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

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

Now JsonConvert.DeserializeObject<Dog>(jsonString)"age"财产丢失。

既然这是你的事always想要,你可以创建一个自定义合约解析器 http://www.newtonsoft.com/json/help/html/contractresolver.htm继承自DefaultContractResolver http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_DefaultContractResolver.htm or CamelCasePropertyNamesContractResolver http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_CamelCasePropertyNamesContractResolver.htm它将自动将传递给构造函数的属性标记为必需,而不需要属性:

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 中缺少属性。

Notes:

  • 这仅在存在时才有效is相应的属性。似乎没有一种简单的方法可以根据需要标记没有相应属性的构造函数参数。

  • 牛顿软件建议您缓存并重用合约解析器以获得最佳性能 https://www.newtonsoft.com/json/help/html/Performance.htm#ReuseContractResolver.

演示小提琴here https://dotnetfiddle.net/KaLsil.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何防止 Json.NET 对缺少的构造函数参数使用默认值,同时仍使用属性的默认值? [关闭] 的相关文章

随机推荐