有没有办法告诉 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 .