Json.net 在构造函数中定义时不使用默认值填充属性
Json.net doesn't populate properties with default values when defined in the constructor
从 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 现在将其视为由构造函数初始化,不再应用 "default value handling" 规则。这似乎来自图书馆的这次更新:link.
将构造函数的参数名称重命名为与属性名称不匹配修复了我的问题,但这似乎不是一个干净的解决方案。是否缺少某些配置属性(或更简洁的方法)?我需要构造函数,因为我希望能够自己创建对象(不是来自 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;
}
}
我不确定这是有意还是无意的改变。您可能想要 report an issue to ask. Or perhaps you 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]
。 (或者甚至使无参数构造函数public并将参数传递给第二个构造函数non-optional。)
从 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 现在将其视为由构造函数初始化,不再应用 "default value handling" 规则。这似乎来自图书馆的这次更新:link.
将构造函数的参数名称重命名为与属性名称不匹配修复了我的问题,但这似乎不是一个干净的解决方案。是否缺少某些配置属性(或更简洁的方法)?我需要构造函数,因为我希望能够自己创建对象(不是来自 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;
}
}
我不确定这是有意还是无意的改变。您可能想要 report an issue to ask. Or perhaps you 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]
。 (或者甚至使无参数构造函数public并将参数传递给第二个构造函数non-optional。)