-Improved deserialization performance by only tracking property presence when needed

This commit is contained in:
James Newton-King
2012-05-06 13:47:23 +12:00
parent e4491e5812
commit 89ab4c0d1e
3 changed files with 85 additions and 46 deletions
+1 -1
View File
@@ -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':");
@@ -72,6 +72,36 @@ namespace Newtonsoft.Json.Serialization
/// <value>The parametrized constructor.</value>
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;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonObjectContract"/> class.
/// </summary>
@@ -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<JsonProperty, PropertyPresence> 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<JsonProperty, PropertyPresence> 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<JsonProperty, PropertyPresence> 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<JsonProperty, PropertyPresence> propertiesPresence)
{
if (propertiesPresence != null)
{
foreach (KeyValuePair<JsonProperty, PropertyPresence> 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<JsonProperty, PropertyPresence> requiredProperties)
{
if (property != null)
if (property != null && requiredProperties != null)
{
requiredProperties[property] = (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.Undefined)
? PropertyPresence.Null