从JSON.Net 6迁移到最新版本(9.0.1)时,我注意到关于DefaultValueHandling.Populate&IgnoreAndPopulate在反序列化期间进行处理。在最新版本中,Json属性在反序列化后被初始化为null,而不是其默认值。
这里有一个简单的测试来重现问题:
private class MyTestClass
{
public const string DefaultText = "...";
[DefaultValue(DefaultText)]
[JsonProperty(PropertyName = "myText", DefaultValueHandling = DefaultValueHandling.Populate)]
public readonly string Text;
public MyTestClass(string text = DefaultText)
{
Text = text;
}
}
[Test]
public void DumbTest()
{
MyTestClass myObject = JsonConvert.DeserializeObject<MyTestClass>("{}"); // Fail with version 9.0.1
Assert.AreEqual(MyTestClass.DefaultText, myObject.Text);
}
发生这种情况是因为构造函数的参数名称与属性名称匹配。因此,Json.net现在认为它是由构造函数初始化的,不再应用"默认值处理"规则。这似乎来自库的更新:链接。
将构造函数的参数名称重命名为与属性名称不匹配可以解决我的问题,但这似乎不是一个干净的解决方案。是否有我缺少的配置属性(或更干净的方式)?我需要构造函数,因为我希望能够自己创建对象(而不是从JSON)。
我能够重现Json 6.0.8到9.0.1的变化。JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters()
现在检查构造函数参数上是否存在[DefaultValue(DefaultText)]
和[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
属性,而不是相应的属性。这项检查是围绕1979年线进行的
if (HasFlag(constructorProperty.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate))
{
context.Value = EnsureType(
reader,
constructorProperty.GetResolvedDefaultValue(),
CultureInfo.InvariantCulture,
constructorProperty.PropertyContract,
constructorProperty.PropertyType);
}
因此,您可以通过如下修改构造函数来成功地反序列化您的类型:
class MyTestClass
{
public const string DefaultText = "...";
[DefaultValue(DefaultText)]
[JsonProperty(PropertyName = "myText", DefaultValueHandling = DefaultValueHandling.Populate)]
public readonly string Text;
public MyTestClass([JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate), DefaultValue(DefaultText)] string text = DefaultText)
{
Text = text;
}
}
我不确定这是有意还是无意的改变。您可能需要报告问题以进行询问。或者你已经有了?
更新
作为另一种解决方法,您可以为您的类型提供一个正确设置默认值的私有无参数构造函数,并用[JsonConstructor]
:标记它
class MyTestClass
{
public const string DefaultText = "...";
[DefaultValue(DefaultText)]
[JsonProperty(PropertyName = "myText", DefaultValueHandling = DefaultValueHandling.Populate)]
public readonly string Text;
[JsonConstructor]
MyTestClass()
: this(DefaultText)
{
}
public MyTestClass(string text = DefaultText)
{
Text = text;
}
}
Json.NET现在将调用私有的无参数构造函数,然后通过反射正确地设置只读字段Text
的值,因为它已经用[JsonProperty]
标记。(或者甚至将无参数构造函数公开,并使第二个构造函数的参数不可选。)