diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs
index 0b532f29..82e20377 100644
--- a/Src/Newtonsoft.Json/JsonObjectAttribute.cs
+++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs
@@ -51,9 +51,9 @@ namespace Newtonsoft.Json
}
///
- /// Gets or sets the null value handling.
+ /// Gets or sets how the object's properties with null values are handled during serialization and deserialization.
///
- /// The null value handling.
+ /// How the object's properties with null values are handled during serialization and deserialization.
public NullValueHandling ItemNullValueHandling
{
get => _itemNullValueHandling ?? default(NullValueHandling);
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
index 44858f72..d1d4180f 100644
--- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
+++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
@@ -327,6 +327,8 @@ namespace Newtonsoft.Json.Serialization
if (attribute != null)
{
contract.ItemRequired = attribute._itemRequired;
+ contract.ItemNullValueHandling = attribute._itemNullValueHandling;
+
if (attribute.NamingStrategyType != null)
{
NamingStrategy namingStrategy = JsonTypeReflector.GetContainerNamingStrategy(attribute);
@@ -1509,11 +1511,6 @@ namespace Newtonsoft.Json.Serialization
#endif
}
- if (property.NullValueHandling == null && containerAttribute is JsonObjectAttribute objectAttribute)
- {
- property.NullValueHandling = objectAttribute._itemNullValueHandling;
- }
-
if (requiredAttribute != null)
{
property._required = Required.Always;
diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs
index c80c01b2..aeb5410e 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs
@@ -52,6 +52,12 @@ namespace Newtonsoft.Json.Serialization
///
public Required? ItemRequired { get; set; }
+ ///
+ /// Gets or sets how the object's properties with null values are handled during serialization and deserialization.
+ ///
+ /// How the object's properties with null values are handled during serialization and deserialization.
+ public NullValueHandling? ItemNullValueHandling { get; set; }
+
///
/// Gets the object's properties.
///
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs
index 1287eb39..73d8670b 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs
@@ -81,6 +81,16 @@ namespace Newtonsoft.Json.Serialization
}
}
+ protected NullValueHandling ResolvedNullValueHandling(JsonObjectContract containerContract, JsonProperty property)
+ {
+ NullValueHandling resolvedNullValueHandling =
+ property.NullValueHandling
+ ?? containerContract?.ItemNullValueHandling
+ ?? Serializer._nullValueHandling;
+
+ return resolvedNullValueHandling;
+ }
+
private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error)
{
if (_currentErrorContext == null)
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs
index 414cfef0..20c16126 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs
@@ -1012,7 +1012,7 @@ namespace Newtonsoft.Json.Serialization
// otherwise also set it if CreateValue returns a new value compared to the currentValue
// this could happen because of a JsonConverter against the type
if ((!useExistingValue || value != currentValue)
- && ShouldSetPropertyValue(property, value))
+ && ShouldSetPropertyValue(property, containerContract as JsonObjectContract, value))
{
property.ValueProvider.SetValue(target, value);
@@ -1076,7 +1076,7 @@ namespace Newtonsoft.Json.Serialization
}
// test tokenType here because null might not be convertible to some types, e.g. ignoring null when applied to DateTime
- if (property.NullValueHandling.GetValueOrDefault(Serializer._nullValueHandling) == NullValueHandling.Ignore && tokenType == JsonToken.Null)
+ if (tokenType == JsonToken.Null && ResolvedNullValueHandling(containerContract as JsonObjectContract, property) == NullValueHandling.Ignore)
{
return true;
}
@@ -1129,9 +1129,9 @@ namespace Newtonsoft.Json.Serialization
return ((value & flag) == flag);
}
- private bool ShouldSetPropertyValue(JsonProperty property, object value)
+ private bool ShouldSetPropertyValue(JsonProperty property, JsonObjectContract contract, object value)
{
- if (property.NullValueHandling.GetValueOrDefault(Serializer._nullValueHandling) == NullValueHandling.Ignore && value == null)
+ if (value == null && ResolvedNullValueHandling(contract, property) == NullValueHandling.Ignore)
{
return false;
}
@@ -1992,7 +1992,7 @@ namespace Newtonsoft.Json.Serialization
JsonProperty property = context.Property;
object value = context.Value;
- if (ShouldSetPropertyValue(property, value))
+ if (ShouldSetPropertyValue(property, contract, value))
{
property.ValueProvider.SetValue(createdObject, value);
context.Used = true;
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs
index 36572dc2..51d8b2f9 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs
@@ -270,10 +270,9 @@ namespace Newtonsoft.Json.Serialization
return Serializer.GetReferenceResolver().IsReferenced(this, value);
}
- private bool ShouldWriteProperty(object memberValue, JsonProperty property)
+ private bool ShouldWriteProperty(object memberValue, JsonObjectContract containerContract, JsonProperty property)
{
- if (property.NullValueHandling.GetValueOrDefault(Serializer._nullValueHandling) == NullValueHandling.Ignore &&
- memberValue == null)
+ if (memberValue == null && ResolvedNullValueHandling(containerContract, property) == NullValueHandling.Ignore)
{
return false;
}
@@ -525,7 +524,7 @@ namespace Newtonsoft.Json.Serialization
memberValue = property.ValueProvider.GetValue(value);
memberContract = (property.PropertyContract.IsSealed) ? property.PropertyContract : GetContractSafe(memberValue);
- if (ShouldWriteProperty(memberValue, property))
+ if (ShouldWriteProperty(memberValue, contract as JsonObjectContract, property))
{
if (ShouldWriteReference(memberValue, property, memberContract, contract, member))
{