ObjectWithParameterizedConstructorConverter<T>
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace System.Text.Json.Serialization.Converters
{
internal abstract class ObjectWithParameterizedConstructorConverter<T> : ObjectDefaultConverter<T>
{
internal override bool ConstructorIsParameterized => true;
internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value)
{
object obj;
if (state.UseFastPath) {
ReadOnlySpan<byte> originalSpan = reader.OriginalSpan;
ReadConstructorArguments(ref state, ref reader, options);
obj = CreateObject(ref state.Current);
if (state.Current.PropertyIndex > 0) {
for (int i = 0; i < state.Current.PropertyIndex; i++) {
JsonPropertyInfo item = state.Current.CtorArgumentState.FoundProperties[i].Item1;
long item2 = state.Current.CtorArgumentState.FoundProperties[i].Item3;
byte[] item3 = state.Current.CtorArgumentState.FoundProperties[i].Item4;
string item4 = state.Current.CtorArgumentState.FoundProperties[i].Item5;
Utf8JsonReader reader2 = new Utf8JsonReader(originalSpan.Slice(checked((int)item2)), true, state.Current.CtorArgumentState.FoundProperties[i].Item2);
state.Current.JsonPropertyName = item3;
state.Current.JsonPropertyInfo = item;
bool flag = item4 != null;
if (flag) {
state.Current.JsonPropertyNameAsString = item4;
JsonSerializer.CreateDataExtensionProperty(obj, item);
}
ReadPropertyValue(obj, ref state, ref reader2, item, flag);
}
ArrayPool<(JsonPropertyInfo, JsonReaderState, long, byte[], string)>.Shared.Return(state.Current.CtorArgumentState.FoundProperties, true);
}
} else {
if (state.Current.ObjectState == StackFrameObjectState.None) {
state.Current.ObjectState = StackFrameObjectState.StartToken;
BeginRead(ref state, ref reader, options);
}
if (!ReadConstructorArgumentsWithContinuation(ref state, ref reader, options)) {
value = default(T);
return false;
}
obj = CreateObject(ref state.Current);
if (state.Current.CtorArgumentState.FoundPropertyCount > 0) {
for (int j = 0; j < state.Current.CtorArgumentState.FoundPropertyCount; j++) {
JsonPropertyInfo item5 = state.Current.CtorArgumentState.FoundPropertiesAsync[j].Item1;
object item6 = state.Current.CtorArgumentState.FoundPropertiesAsync[j].Item2;
string item7 = state.Current.CtorArgumentState.FoundPropertiesAsync[j].Item3;
if (item7 == null)
item5.SetExtensionDictionaryAsObject(obj, item6);
else {
JsonSerializer.CreateDataExtensionProperty(obj, item5);
object valueAsObject = item5.GetValueAsObject(obj);
IDictionary<string, JsonElement> dictionary = valueAsObject as IDictionary<string, JsonElement>;
if (dictionary != null)
dictionary[item7] = (JsonElement)item6;
else
((IDictionary<string, object>)valueAsObject)[item7] = item6;
}
}
ArrayPool<(JsonPropertyInfo, object, string)>.Shared.Return(state.Current.CtorArgumentState.FoundPropertiesAsync, true);
}
}
if (state.Current.PropertyRefCache != null)
state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current);
if (state.Current.CtorArgumentState.ParameterRefCache != null)
state.Current.JsonClassInfo.UpdateSortedParameterCache(ref state.Current);
EndRead(ref state);
value = (T)obj;
return true;
}
protected abstract void InitializeConstructorArgumentCaches(ref ReadStack state, JsonSerializerOptions options);
protected abstract bool ReadAndCacheConstructorArgument(ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo);
protected abstract object CreateObject(ref ReadStackFrame frame);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadConstructorArguments(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options)
{
BeginRead(ref state, ref reader, options);
while (true) {
ref reader.ReadWithVerify();
JsonTokenType tokenType = reader.TokenType;
if (tokenType == JsonTokenType.EndObject)
break;
if (TryLookupConstructorParameter(ref state, ref reader, options, out JsonParameterInfo jsonParameterInfo)) {
ref reader.ReadWithVerify();
if (!jsonParameterInfo.ShouldDeserialize) {
reader.Skip();
state.Current.EndConstructorParameter();
} else {
ReadAndCacheConstructorArgument(ref state, ref reader, jsonParameterInfo);
state.Current.EndConstructorParameter();
}
} else {
ReadOnlySpan<byte> propertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options);
bool useExtensionProperty;
JsonPropertyInfo item = JsonSerializer.LookupProperty(null, propertyName, ref state, out useExtensionProperty, false);
if (state.Current.CtorArgumentState.FoundProperties == null)
state.Current.CtorArgumentState.FoundProperties = ArrayPool<(JsonPropertyInfo, JsonReaderState, long, byte[], string)>.Shared.Rent(Math.Max(1, state.Current.JsonClassInfo.PropertyCache.Count));
else if (state.Current.PropertyIndex - 1 == state.Current.CtorArgumentState.FoundProperties.Length) {
(JsonPropertyInfo, JsonReaderState, long, byte[], string)[] array = ArrayPool<(JsonPropertyInfo, JsonReaderState, long, byte[], string)>.Shared.Rent(state.Current.CtorArgumentState.FoundProperties.Length * 2);
state.Current.CtorArgumentState.FoundProperties.CopyTo(array, 0);
ArrayPool<(JsonPropertyInfo, JsonReaderState, long, byte[], string)>.Shared.Return(state.Current.CtorArgumentState.FoundProperties, true);
state.Current.CtorArgumentState.FoundProperties = array;
}
state.Current.CtorArgumentState.FoundProperties[state.Current.PropertyIndex - 1] = (item, reader.CurrentState, reader.BytesConsumed, state.Current.JsonPropertyName, state.Current.JsonPropertyNameAsString);
reader.Skip();
state.Current.EndProperty();
}
}
}
private bool ReadConstructorArgumentsWithContinuation(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options)
{
while (true) {
if (state.Current.PropertyState == StackFramePropertyState.None) {
state.Current.PropertyState = StackFramePropertyState.ReadName;
if (!reader.Read())
return false;
}
JsonParameterInfo jsonParameterInfo;
JsonPropertyInfo jsonPropertyInfo;
if ((int)state.Current.PropertyState < 2) {
state.Current.PropertyState = StackFramePropertyState.Name;
JsonTokenType tokenType = reader.TokenType;
if (tokenType == JsonTokenType.EndObject)
return true;
if (TryLookupConstructorParameter(ref state, ref reader, options, out jsonParameterInfo))
jsonPropertyInfo = null;
else {
ReadOnlySpan<byte> propertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options);
jsonPropertyInfo = JsonSerializer.LookupProperty(null, propertyName, ref state, out bool useExtensionProperty, false);
state.Current.UseExtensionProperty = useExtensionProperty;
}
} else {
jsonParameterInfo = state.Current.CtorArgumentState.JsonParameterInfo;
jsonPropertyInfo = state.Current.JsonPropertyInfo;
}
if (jsonParameterInfo != null) {
if (!HandleConstructorArgumentWithContinuation(ref state, ref reader, jsonParameterInfo))
return false;
} else if (!HandlePropertyWithContinuation(ref state, ref reader, jsonPropertyInfo)) {
break;
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool HandleConstructorArgumentWithContinuation(ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo)
{
if ((int)state.Current.PropertyState < 3) {
if (!jsonParameterInfo.ShouldDeserialize) {
if (!reader.TrySkip())
return false;
state.Current.EndConstructorParameter();
return true;
}
state.Current.PropertyState = StackFramePropertyState.ReadValue;
if (!JsonConverter.SingleValueReadWithReadAhead(jsonParameterInfo.ConverterBase.ClassType, ref reader, ref state))
return false;
}
if (!ReadAndCacheConstructorArgument(ref state, ref reader, jsonParameterInfo))
return false;
state.Current.EndConstructorParameter();
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool HandlePropertyWithContinuation(ref ReadStack state, ref Utf8JsonReader reader, JsonPropertyInfo jsonPropertyInfo)
{
if ((int)state.Current.PropertyState < 3) {
if (!jsonPropertyInfo.ShouldDeserialize) {
if (!reader.TrySkip())
return false;
state.Current.EndProperty();
return true;
}
if (!ReadAheadPropertyValue(ref state, ref reader, jsonPropertyInfo))
return false;
}
object value;
if (state.Current.UseExtensionProperty) {
if (!jsonPropertyInfo.ReadJsonExtensionDataValue(ref state, ref reader, out value))
return false;
} else if (!jsonPropertyInfo.ReadJsonAsObject(ref state, ref reader, out value)) {
return false;
}
if (state.Current.CtorArgumentState.FoundPropertiesAsync == null)
state.Current.CtorArgumentState.FoundPropertiesAsync = ArrayPool<(JsonPropertyInfo, object, string)>.Shared.Rent(Math.Max(1, state.Current.JsonClassInfo.PropertyCache.Count));
else if (state.Current.CtorArgumentState.FoundPropertyCount == state.Current.CtorArgumentState.FoundPropertiesAsync.Length) {
(JsonPropertyInfo, object, string)[] array = ArrayPool<(JsonPropertyInfo, object, string)>.Shared.Rent(state.Current.CtorArgumentState.FoundPropertiesAsync.Length * 2);
state.Current.CtorArgumentState.FoundPropertiesAsync.CopyTo(array, 0);
ArrayPool<(JsonPropertyInfo, object, string)>.Shared.Return(state.Current.CtorArgumentState.FoundPropertiesAsync, true);
state.Current.CtorArgumentState.FoundPropertiesAsync = array;
}
state.Current.CtorArgumentState.FoundPropertiesAsync[state.Current.CtorArgumentState.FoundPropertyCount++] = (jsonPropertyInfo, value, state.Current.JsonPropertyNameAsString);
state.Current.EndProperty();
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BeginRead(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
if (state.Current.JsonClassInfo.ParameterCount != state.Current.JsonClassInfo.ParameterCache.Count)
ThrowHelper.ThrowInvalidOperationException_ConstructorParameterIncompleteBinding(base.ConstructorInfo, TypeToConvert);
state.Current.JsonPropertyInfo = null;
InitializeConstructorArgumentCaches(ref state, options);
}
protected virtual void EndRead(ref ReadStack state)
{
}
protected virtual bool TryLookupConstructorParameter(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options, out JsonParameterInfo jsonParameterInfo)
{
ReadOnlySpan<byte> propertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options);
jsonParameterInfo = state.Current.JsonClassInfo.GetParameter(propertyName, ref state.Current, out byte[] utf8PropertyName);
state.Current.CtorArgumentState.ParameterIndex++;
state.Current.JsonPropertyName = utf8PropertyName;
state.Current.CtorArgumentState.JsonParameterInfo = jsonParameterInfo;
return jsonParameterInfo != null;
}
}
}