diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs
index a2c6dc0d..98ed8101 100644
--- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs
+++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs
@@ -727,7 +727,7 @@ now brown cow?", '"', true);
{
StringBuilder sb = new StringBuilder();
- int depth = 700;
+ int depth = 900;
for (int i = 0; i < depth; i++)
{
sb.Append("{'A':");
diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs
index 86d98d65..2a424d35 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs
@@ -72,6 +72,36 @@ namespace Newtonsoft.Json.Serialization
/// The parametrized constructor.
public ConstructorInfo ParametrizedConstructor { get; set; }
+ private bool? _hasRequiredOrDefaultValueProperties;
+ internal bool HasRequiredOrDefaultValueProperties
+ {
+ get
+ {
+ if (_hasRequiredOrDefaultValueProperties == null)
+ {
+ _hasRequiredOrDefaultValueProperties = false;
+
+ if (ItemRequired.GetValueOrDefault(Required.Default) != Required.Default)
+ {
+ _hasRequiredOrDefaultValueProperties = true;
+ }
+ else
+ {
+ foreach (JsonProperty property in Properties)
+ {
+ if (property.Required != Required.Default || ((property.DefaultValueHandling & DefaultValueHandling.Populate) == DefaultValueHandling.Populate) && property.Writable)
+ {
+ _hasRequiredOrDefaultValueProperties = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return _hasRequiredOrDefaultValueProperties.Value;
+ }
+ }
+
///
/// Initializes a new instance of the class.
///
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs
index 3a9bbd6b..d4bf1d9d 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs
@@ -88,7 +88,7 @@ namespace Newtonsoft.Json.Serialization
if (contract.ContractType == JsonContractType.Dictionary)
PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract) contract, null, null, id);
else if (contract.ContractType == JsonContractType.Object)
- PopulateObject(target, reader, (JsonObjectContract) contract, null, null, id);
+ PopulateObject(target, reader, (JsonObjectContract) contract, null, id);
else
throw JsonSerializationException.Create(reader, "Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType));
}
@@ -317,7 +317,7 @@ namespace Newtonsoft.Json.Serialization
else
targetObject = CreateNewObject(reader, objectContract, member, containerMember, id);
- return PopulateObject(targetObject, reader, objectContract, member, containerMember, id);
+ return PopulateObject(targetObject, reader, objectContract, member, id);
case JsonContractType.Primitive:
JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract) contract;
// if the content is inside $value then read past it
@@ -1245,12 +1245,14 @@ To fix this error either change the environment to be fully trusted, change the
return newObject;
}
- private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, JsonProperty containerProperty, string id)
+ private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, string id)
{
contract.InvokeOnDeserializing(newObject, Serializer.Context);
- Dictionary propertiesPresence =
- contract.Properties.ToDictionary(m => m, m => PropertyPresence.None);
+ // only need to keep a track of properies presence if they are required or a value should be defaulted if missing
+ Dictionary propertiesPresence = (contract.HasRequiredOrDefaultValueProperties || HasFlag(Serializer.DefaultValueHandling, DefaultValueHandling.Populate))
+ ? contract.Properties.ToDictionary(m => m, m => PropertyPresence.None)
+ : null;
if (id != null)
Serializer.ReferenceResolver.AddReference(this, id, newObject);
@@ -1303,45 +1305,7 @@ To fix this error either change the environment to be fully trusted, change the
break;
case JsonToken.EndObject:
{
- foreach (KeyValuePair propertyPresence in propertiesPresence)
- {
- JsonProperty property = propertyPresence.Key;
- PropertyPresence presence = propertyPresence.Value;
-
- if (presence == PropertyPresence.None || presence == PropertyPresence.Null)
- {
- try
- {
- Required resolvedRequired = property._required ?? contract.ItemRequired ?? Required.Default;
-
- switch (presence)
- {
- case PropertyPresence.None:
- if (resolvedRequired == Required.AllowNull || resolvedRequired == Required.Always)
- throw JsonSerializationException.Create(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
-
- if (property.PropertyContract == null)
- property.PropertyContract = GetContractSafe(property.PropertyType);
-
- if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Populate)
- && property.Writable)
- property.ValueProvider.SetValue(newObject, EnsureType(reader, property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType));
- break;
- case PropertyPresence.Null:
- if (resolvedRequired == Required.Always)
- throw JsonSerializationException.Create(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
- break;
- }
- }
- catch (Exception ex)
- {
- if (IsErrorHandled(newObject, contract, property.PropertyName, reader.Path, ex))
- HandleError(reader, initialDepth);
- else
- throw;
- }
- }
- }
+ EndObject(newObject, reader, contract, initialDepth, propertiesPresence);
contract.InvokeOnDeserialized(newObject, Serializer.Context);
return newObject;
@@ -1357,9 +1321,54 @@ To fix this error either change the environment to be fully trusted, change the
throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object.");
}
+ private void EndObject(object newObject, JsonReader reader, JsonObjectContract contract, int initialDepth, Dictionary propertiesPresence)
+ {
+ if (propertiesPresence != null)
+ {
+ foreach (KeyValuePair propertyPresence in propertiesPresence)
+ {
+ JsonProperty property = propertyPresence.Key;
+ PropertyPresence presence = propertyPresence.Value;
+
+ if (presence == PropertyPresence.None || presence == PropertyPresence.Null)
+ {
+ try
+ {
+ Required resolvedRequired = property._required ?? contract.ItemRequired ?? Required.Default;
+
+ switch (presence)
+ {
+ case PropertyPresence.None:
+ if (resolvedRequired == Required.AllowNull || resolvedRequired == Required.Always)
+ throw JsonSerializationException.Create(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
+
+ if (property.PropertyContract == null)
+ property.PropertyContract = GetContractSafe(property.PropertyType);
+
+ if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Populate) && property.Writable)
+ property.ValueProvider.SetValue(newObject, EnsureType(reader, property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType));
+ break;
+ case PropertyPresence.Null:
+ if (resolvedRequired == Required.Always)
+ throw JsonSerializationException.Create(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ if (IsErrorHandled(newObject, contract, property.PropertyName, reader.Path, ex))
+ HandleError(reader, initialDepth);
+ else
+ throw;
+ }
+ }
+ }
+ }
+ }
+
private void SetPropertyPresence(JsonReader reader, JsonProperty property, Dictionary requiredProperties)
{
- if (property != null)
+ if (property != null && requiredProperties != null)
{
requiredProperties[property] = (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.Undefined)
? PropertyPresence.Null