如何防止Json.NET在仍然使用默认值作为属性的同时,将默认值用于缺少的构造函数参数? [英] How to prevent Json.NET from using use default values for missing constructor parameters while still using default values for properties?

查看:111
本文介绍了如何防止Json.NET在仍然使用默认值作为属性的同时,将默认值用于缺少的构造函数参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Is there a way to tell JSON.net that when it attempts to deserialize using a constructor (if there is no default constructor), that it should NOT assign default value to constructor parameters and that it should only call a constructor if every constructor parameter is represented in the JSON string? This same serializer SHOULD use default values when calling property/field setters, the rule is only scoped to constructors. None of the enum values here seem to be appropriate: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_DefaultValueHandling.htm

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

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

for example, the json string "{}" will deserialize to an object of type Dog by setting the Dog's age to 0 (the default value for an int). I'd like to a generalized, not-attribute-based solution to prevent this from happening. In this case, {"age":4} would work because age is specified in the JSON string and corresponds to the constructor parameter.

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

    public int Age { get; }
}

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

However, if Dog is specified as such, then "{}" should deserialize to a Dog with Age == 0, because the Dog is not being created using a constructor.

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

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

As to "why would you want to do this"... Objects with constructors are typically qualitatively different than POCOs as it relates to their properties. Using a constructor to store property values instead of settable properties on a POCO typically means that you want to validate/constrain the property values. So it's reasonable not to allow deserialization with default values in the presence of constructor(s).

推荐答案

当Json.NET遇到一个没有无参数构造函数但带有参数化构造函数的对象时,它将调用该构造函数来创建对象,

When Json.NET encounters an object without a parameterless constructor but with a parameterized constructor, it will call that constructor to create the object, matching the JSON property names to the constructor arguments by name using reflection via a case-insensitive best match algorithm. I.e. a property whose name also appears in the constructor will be set via the constructor call, not the set method (even if there is one).

因此,您可以通过将等效属性标记为必需:

Thus, you can mark a constructor argument as required by marking the equivalent property as required:

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

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

现在,当缺少"age"属性时,将抛出JsonConvert.DeserializeObject<Dog>(jsonString).

Now JsonConvert.DeserializeObject<Dog>(jsonString) will throw when the "age" property is missing.

由于这是您始终想要的东西,因此可以创建 DefaultContractResolver CamelCasePropertyNamesContractResolver 将传递给构造函数的属性标记为自动需要,无需属性:

Since this is something you always want, you can create a custom contract resolver inheriting from DefaultContractResolver or CamelCasePropertyNamesContractResolver that marks properties passed to the constructor as required automatically, without the need for attributes:

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;
    }
}

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

Then construct an instance of the resolver:

static IContractResolver requiredResolver = new ConstructorParametersRequiredContractResolver();

并按如下所示使用它:

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

如果JSON中缺少"age"属性,则将立即反序列化.

Now deserialization will throw if the "age" property is missing from the JSON.

注意:

  • 这仅在具有 对应属性的情况下有效.似乎没有一种简单的方法可以根据需要标记没有相应属性的构造函数参数.

  • This only works if there is a corresponding property. There doesn't appear to be a straightforward way to mark a constructor parameter with no corresponding property as required.

Newtonsoft建议您缓存并重用合同解析器以获得最佳性能.

Newtonsoft recommends that you cache and reuse the contract resolver for best performance.

演示小提琴此处.

这篇关于如何防止Json.NET在仍然使用默认值作为属性的同时,将默认值用于缺少的构造函数参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆