in src/Elastic.Apm/Libraries/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs [1758:1980]
private object CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty? containerProperty,
ObjectConstructor<object> creator, string? id
)
{
ValidationUtils.ArgumentNotNull(creator, nameof(creator));
// only need to keep a track of properties' presence if they are required or a value should be defaulted if missing
var trackPresence = contract.HasRequiredOrDefaultValueProperties
|| HasFlag(Serializer._defaultValueHandling, DefaultValueHandling.Populate);
var objectType = contract.UnderlyingType;
if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
{
var parameters = string.Join(", ", contract.CreatorParameters.Select(p => p.PropertyName)
#if !HAVE_STRING_JOIN_WITH_ENUMERABLE
.ToArray()
#endif
);
TraceWriter.Trace(TraceLevel.Info,
JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path,
"Deserializing {0} using creator with parameters: {1}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType,
parameters)), null);
}
var propertyContexts = ResolvePropertyAndCreatorValues(contract, containerProperty, reader, objectType);
if (trackPresence)
{
foreach (var property in contract.Properties)
{
if (!property.Ignored)
{
if (propertyContexts.All(p => p.Property != property))
{
propertyContexts.Add(new CreatorPropertyContext(property.PropertyName!)
{
Property = property, Presence = PropertyPresence.None
});
}
}
}
}
var creatorParameterValues = new object?[contract.CreatorParameters.Count];
foreach (var context in propertyContexts)
{
// set presence of read values
if (trackPresence)
{
if (context.Property != null && context.Presence == null)
{
var v = context.Value;
PropertyPresence propertyPresence;
if (v == null)
propertyPresence = PropertyPresence.Null;
else if (v is string s)
{
propertyPresence = CoerceEmptyStringToNull(context.Property.PropertyType, context.Property.PropertyContract, s)
? PropertyPresence.Null
: PropertyPresence.Value;
}
else
propertyPresence = PropertyPresence.Value;
context.Presence = propertyPresence;
}
}
var constructorProperty = context.ConstructorProperty;
if (constructorProperty == null && context.Property != null)
constructorProperty =
contract.CreatorParameters.ForgivingCaseSensitiveFind(p => p.PropertyName!, context.Property.UnderlyingName!);
if (constructorProperty != null && !constructorProperty.Ignored)
{
// handle giving default values to creator parameters
// this needs to happen before the call to creator
if (trackPresence)
{
if (context.Presence == PropertyPresence.None || context.Presence == PropertyPresence.Null)
{
if (constructorProperty.PropertyContract == null)
constructorProperty.PropertyContract = GetContractSafe(constructorProperty.PropertyType);
if (HasFlag(constructorProperty.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling),
DefaultValueHandling.Populate))
{
context.Value = EnsureType(
reader,
constructorProperty.GetResolvedDefaultValue(),
CultureInfo.InvariantCulture,
constructorProperty.PropertyContract!,
constructorProperty.PropertyType);
}
}
}
var i = contract.CreatorParameters.IndexOf(constructorProperty);
creatorParameterValues[i] = context.Value;
context.Used = true;
}
}
var createdObject = creator(creatorParameterValues);
if (id != null) AddReference(reader, id, createdObject);
OnDeserializing(reader, contract, createdObject);
// go through unused values and set the newly created object's properties
foreach (var context in propertyContexts)
{
if (context.Used ||
context.Property == null ||
context.Property.Ignored ||
context.Presence == PropertyPresence.None)
continue;
var property = context.Property;
var value = context.Value;
if (ShouldSetPropertyValue(property, contract, value))
{
property.ValueProvider!.SetValue(createdObject, value);
context.Used = true;
}
else if (!property.Writable && value != null)
{
// handle readonly collection/dictionary properties
var propertyContract = Serializer._contractResolver.ResolveContract(property.PropertyType!);
if (propertyContract.ContractType == JsonContractType.Array)
{
var propertyArrayContract = (JsonArrayContract)propertyContract;
if (propertyArrayContract.CanDeserialize && !propertyArrayContract.IsReadOnlyOrFixedSize)
{
var createdObjectCollection = property.ValueProvider!.GetValue(createdObject);
if (createdObjectCollection != null)
{
propertyArrayContract = (JsonArrayContract)GetContract(createdObjectCollection.GetType());
var createdObjectCollectionWrapper = propertyArrayContract.ShouldCreateWrapper
? propertyArrayContract.CreateWrapper(createdObjectCollection)
: (IList)createdObjectCollection;
// Don't attempt to populate array/read-only list
if (!createdObjectCollectionWrapper.IsFixedSize)
{
var newValues = propertyArrayContract.ShouldCreateWrapper
? propertyArrayContract.CreateWrapper(value)
: (IList)value;
foreach (var newValue in newValues) createdObjectCollectionWrapper.Add(newValue);
}
}
}
}
else if (propertyContract.ContractType == JsonContractType.Dictionary)
{
var dictionaryContract = (JsonDictionaryContract)propertyContract;
if (!dictionaryContract.IsReadOnlyOrFixedSize)
{
var createdObjectDictionary = property.ValueProvider!.GetValue(createdObject);
if (createdObjectDictionary != null)
{
var targetDictionary = dictionaryContract.ShouldCreateWrapper
? dictionaryContract.CreateWrapper(createdObjectDictionary)
: (IDictionary)createdObjectDictionary;
var newValues = dictionaryContract.ShouldCreateWrapper ? dictionaryContract.CreateWrapper(value) : (IDictionary)value;
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
var e = newValues.GetEnumerator();
try
{
while (e.MoveNext())
{
var entry = e.Entry;
targetDictionary[entry.Key] = entry.Value;
}
}
finally
{
(e as IDisposable)?.Dispose();
}
}
}
}
context.Used = true;
}
}
if (contract.ExtensionDataSetter != null)
{
foreach (var propertyValue in propertyContexts)
if (!propertyValue.Used && propertyValue.Presence != PropertyPresence.None)
contract.ExtensionDataSetter(createdObject, propertyValue.Name, propertyValue.Value);
}
if (trackPresence)
{
foreach (var context in propertyContexts)
{
if (context.Property == null) continue;
EndProcessProperty(
createdObject,
reader,
contract,
reader.Depth,
context.Property,
context.Presence.GetValueOrDefault(),
!context.Used);
}
}
OnDeserialized(reader, contract, createdObject);
return createdObject;
}