在构造函数中定义时,Json.net不会使用默认值填充属性

时间:2017-02-02 15:49:50

标签: c# json json.net deserialization json-deserialization

从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现在认为它是由构造函数初始化的,并且不再应用&#34;默认值处理&#34;规则。这似乎来自这个库的更新:link

重命名构造函数的参数名称与属性的名称不匹配修复了我的问题,但它似乎不是一个干净的解决方案。是否有一些我缺少的配置属性(或更清洁的方式)?我需要构造函数,因为我希望能够自己创建对象(而不是来自JSON)。

1 个答案:

答案 0 :(得分:0)

我能够重现从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;
    }
}

我不确定这是有意还是无意的变化。您可能希望report an issue询问。或许你already have

<强>更新

作为另一种解决方法,您可以为您的类型提供一个私有无参数构造函数,以正确设置默认值,并使用[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]。 (或者甚至使无参数构造函数公开,并使第二个构造函数的参数非可选。)