【问题标题】:Json.net doesn't populate properties with default values when defined in the constructor在构造函数中定义时,Json.net 不会使用默认值填充属性
【发布时间】:2017-02-02 15:49:49
【问题描述】:

在从 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 现在将其视为由构造函数初始化,并且不再应用“默认值处理”规则。这似乎来自图书馆的这次更新:link

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

【问题讨论】:

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


    【解决方案1】:

    我能够重现从 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]。 (或者甚至将无参数构造函数设为公开,并将第二个构造函数的参数设为非可选。)

    【讨论】:

    • 谢谢。是的,我在调查时报告了this issue,看起来现在已经确定了根本原因。一些解决方法是可能的,但它对我来说绝对是一个错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2017-10-26
    • 1970-01-01
    • 2016-07-27
    • 1970-01-01
    • 2018-11-03
    相关资源
    最近更新 更多