我的服务器响应由一组已知和未知属性组成。对于已知的属性,我创建了一个 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 值。 值是Include
和Ignore
。DefaultValueHandling
. 这具有更详细的语义:-
Include
:在序列化对象时,包括成员值与成员默认值相同的成员。包含的成员将写入 JSON。反序列化时不起作用。 -
Ignore
:在序列化对象时,如果成员值与成员的默认值相同,则忽略成员,以便不会将其写入 JSON。此选项将忽略所有默认值(例如,对于对象和可为空的类型为 null;对于整数、小数和浮点数为 0;对于布尔值为 false)。 -
Populate
:反序列化时,具有默认值但没有 JSON 的成员将设置为其默认值。 -
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.
}
原型小提琴。