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

ren*_*ene 3 c# serialization json.net

我的服务器响应包含一组已知和未知的属性.对于已知的,我创建了一个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; }
}
Run Code Online (Sandbox Code Playgroud)

Null被允许作为值KnownProperty.如果我尝试反序列化包含的JSON对象,如果我配置了序列化程序,则KNOWN_PROPERTY : null该属性也包含在字典中.即使存在以下类成员,也会执行此操作:UnknownPropertiesNullValueHandling.IgnoreKNOWN_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());
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

输出:

 KNOWN_PROPERTY : null
 UNKNOWN_PROPERTY : null
Run Code Online (Sandbox Code Playgroud)

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

[ExtensionData]如果NullValueHandling设置为忽略,我的理解是无法正常工作,因为文档声明只有在找不到匹配的类成员时才使用扩展.

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

我正在使用Json.NET 8.0.2

dbc*_*dbc 5

更新

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • NullValueHandling.指定在序列化和反序列化对象时包含或忽略空值.价值观IncludeIgnore.

  • DefaultValueHandling.这有更复杂的语义:

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

那么,这些重叠设置如何相互作用?事实证明,Json.NET同时检查它们:如果两个设置都一致则序列化,如果两个设置都令人满意则反序列化.而且DefaultValueHandling.IgnoreAndPopulate似乎你想要做什么-序列化时,它忽略了空,但读取和反序列化时,如果存在设置它们.

因此,我能够通过以下方式获得您想要的行为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.
}
Run Code Online (Sandbox Code Playgroud)

原型小提琴.