NullValueHandling.Ignore 会影响反序列化为 [ExtensionData],尽管匹配类成员



我的服务器响应由一组已知和未知属性组成。对于已知的属性,我创建了一个 DTO 类,其中包含每个属性的成员。未知属性应放在用 [ExtensionData] 属性注释的字典中:

[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Dto
{
    [JsonExtensionData]
    private readonly Dictionary<string, object> unknownProperties = new Dictionary<string, object>();
    public IDictionary<string, object> UnknownProperties
    {
        get
        {
            return new ReadOnlyDictionary<string, object>(this.unknownProperties);
        }
    }
    [JsonProperty(Required = Required.Default, PropertyName = "KNOWN_PROPERTY")]
    public string KnownProperty { get; private set; }
}

允许Null作为KnownProperty的值。如果我尝试反序列化包含 KNOWN_PROPERTY : null 的 JSON 对象,如果我使用 NullValueHandling.Ignore 配置序列化程序,此属性也包含在字典UnknownProperties中。即使存在一个类成员KNOWN_PROPERTY,也会这样做:

static void Main(string[] args)
{
   string jsonString = @"{
          KNOWN_PROPERTY : null,
          UNKNOWN_PROPERTY : null
   }";
   JsonSerializer serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings()
   {
       NullValueHandling = NullValueHandling.Ignore
    });
    using (var textReader = new StringReader(jsonString))
    {
       Dto dto = serializer.Deserialize<Dto>(new JsonTextReader(textReader));
       foreach (var pair in dto.UnknownProperties)
       {
           Console.WriteLine("{0}: {1}", pair.Key, pair.Value == null ? "null" : pair.Value.ToString());
        }
    }
 }

输出:

 KNOWN_PROPERTY : null
 UNKNOWN_PROPERTY : null

如果我使用 NullValueHandling.Include 配置序列化程序或在 JSON 字符串中为 KNOWN_PROPERTY 设置值,则字典仅包含 UNKNOWN_PROPERTY ,正如预期的那样。

据我了解[ExtensionData]如果NullValueHandling设置为忽略,则无法正常工作,因为文档指出仅在找不到匹配的类成员时才使用该扩展。

我看到的行为是有意的吗?我可以做些什么来避免这种情况吗?因为我不喜欢向服务器发送空值,所以我想坚持使用当前设置的NullValueHandling

我正在使用 8.0.2 Json.NET

更新

在 JsonExtensionData 中报告不应包含作为实际对象属性的空值。 #1719 并在提交 e079301 中修复。 此修复应包含在 11.0.2 之后的下一个 Json.NET 版本中。

原始答案

已确认 - JsonSerializerInternalReader.PopulateObject(object, JsonReader, JsonObjectContract, JsonProperty, string)有以下逻辑:

// set extension data if property is ignored or readonly
if (!SetPropertyValue(property, propertyConverter, contract, member, reader, newObject))
{
    SetExtensionData(contract, member, reader, memberName, newObject);
}

如果要忽略属性,其意图似乎是将值放入扩展数据中,但如果忽略该值,则 Json.NET 将其放入扩展数据中 - 这是一个略有不同的概念。 我同意这可能是一个错误。 您可能需要举报。

有一个解决方法。 Json.NET 有两个属性会影响 null/默认值的序列化方式:

  • NullValueHandling . 指定在序列化和反序列化对象时包括或忽略 null 值。 值是IncludeIgnore

  • DefaultValueHandling . 这具有更详细的语义:

    1. Include:在序列化对象时,包括成员值与成员默认值相同的成员。包含的成员将写入 JSON。反序列化时不起作用。
    2. Ignore:在序列化对象时,如果成员值与成员的默认值相同,则忽略成员,以便不会将其写入 JSON。此选项将忽略所有默认值(例如,对于对象和可为空的类型为 null;对于整数、小数和浮点数为 0;对于布尔值为 false)。
    3. Populate:反序列化时,具有默认值但没有 JSON 的成员将设置为其默认值。
    4. IgnoreAndPopulate:序列化对象时忽略成员值与成员默认值相同的成员,反序列化时将成员设置为其默认值。

那么,这些重叠的设置是如何相互作用的呢? 事实证明,Json.NET 检查它们:如果两个设置都同意,则序列化,如果两个设置都同意,则反序列化。 DefaultValueHandling.IgnoreAndPopulate似乎可以做你想做的事——它在序列化时省略 null,但在反序列化时读取和设置它们(如果存在)。

因此,我能够通过以下JsonProperty获得您想要的行为:

public class Dto
{
    [JsonProperty(Required = Required.Default, PropertyName = "KNOWN_PROPERTY", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, NullValueHandling = NullValueHandling.Include)]
    public string KnownProperty { get; private set; }
    // Remainder as before.
}

原型小提琴。

相关内容

  • 没有找到相关文章

最新更新