有没有办法告诉JSON.net当它尝试使用构造函数反序列化时(如果没有默认构造函数),它不应该为构造函数参数赋值默认值,并且它应该只调用构造函数,如果每个构造函数参数是用JSON字符串表示?同一个序列化程序在调用属性/字段设置器时应该使用默认值,规则只限于构造函数.这里的枚举值似乎都不合适:http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_DefaultValueHandling.htm
解决方案不应该依赖于对要反序列化的类型应用任何属性.
例如,通过将Dog的age设置为0(int的默认值),json字符串"{}"将反序列化为类型的对象Dog.我想要一个通用的,不基于属性的解决方案来防止这种情况发生.在这种情况下,{"age":4}将工作因为age在JSON字符串中指定并对应于构造函数参数.
public class Dog
{
public Dog(int age)
{
this.Age = age;
}
public int Age { get; }
}
Run Code Online (Sandbox Code Playgroud)
但是,如果Dog指定为这样,那么"{}" 应该反序列化为具有Age == 0的Dog,因为Dog不是使用构造函数创建的.
public class Dog
{
public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
并且要避免任何关于"你为什么要这样做"的问题......带有构造函数的对象通常在质量上与POCO不同,因为它与它们的属性有关.使用构造函数在POCO上存储属性值而不是可设置属性通常意味着您要验证/约束属性值.因此,在存在constuctor的情况下不允许使用默认值进行反序列化是合理的.
当Json.NET遇到没有无参数构造函数但带有参数化构造函数的对象时,它将调用该构造函数来创建对象,使用反射通过不区分大小写的最佳匹配算法将JSON属性名称与构造函数参数匹配.即一个属性,其名称也出现在构造函数中,将通过构造函数调用设置,而不是set方法(即使有一个).
因此,要求作为标记等价的属性,你可以标记一个构造函数的参数要求:
public class Dog
{
public Dog(int age)
{
this.Age = age;
}
[JsonProperty(Required = Required.Always)]
public int Age { get; }
}
Run Code Online (Sandbox Code Playgroud)
现在JsonConvert.DeserializeObject<Dog>(jsonString)将在"age"财产缺失时抛出.
由于这是您一直想要的,您可以创建一个自定义合约解析程序继承DefaultContractResolver或CamelCasePropertyNamesContractResolver自动标记传递给构造函数的属性,而不需要属性:
public class ConstructorParametersRequiredContractResolver : DefaultContractResolver
{
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
// See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
static ConstructorParametersRequiredContractResolver instance;
static ConstructorParametersRequiredContractResolver() { instance = new ConstructorParametersRequiredContractResolver(); }
public static ConstructorParametersRequiredContractResolver Instance { get { return instance; } }
protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
{
var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);
if (property != null && matchingMemberProperty != null)
{
var required = matchingMemberProperty.Required;
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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后
var settings = new JsonSerializerSettings { ContractResolver = ConstructorParametersRequiredContractResolver.Instance };
JsonConvert.DeserializeObject<T>(jsonString, settings)
Run Code Online (Sandbox Code Playgroud)
将再次抛出.
(请注意,这只是工作,如果有是一个相应的属性.似乎没有被标记为具有无需对应的属性构造函数的参数的简单方法.)
| 归档时间: |
|
| 查看次数: |
942 次 |
| 最近记录: |