private object CreateObjectUsingCreatorWithParameters()

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;
		}