private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary()); if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable. Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.CreateObject is null && classInfo.ClassType == ClassType.Object) { if (classInfo.Type.IsInterface) { ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(classInfo.Type); } else { ThrowHelper.ThrowInvalidOperationException_DeserializeMissingParameterlessConstructor(classInfo.Type); } } if (state.Current.IsProcessingObject(ClassType.Dictionary)) { object value = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.ReturnValue = value; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else { state.Current.ReturnValue = classInfo.CreateObject(); } }
private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructible); if (state.Current.IsProcessingEnumerable) { // A nested object within an enumerable. Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.CreateObject is null && classInfo.ClassType == ClassType.Object) { if (classInfo.Type.IsInterface) { ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(classInfo.Type); } else { ThrowHelper.ThrowInvalidOperationException_DeserializeMissingParameterlessConstructor(classInfo.Type); } } if (state.Current.IsProcessingIDictionaryConstructible) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject(); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Value) { // Custom converter. state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader); HandleEndObject(options, ref reader, ref state); } else { state.Current.ReturnValue = classInfo.CreateObject(); } }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary()); // Note: unless we are a root object, we are going to push a property onto the ReadStack // in the if/else if check below. if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable (non-dictionary). HandleStartObjectInEnumerable(ref state, options, state.Current.JsonPropertyInfo !.DeclaredPropertyType); } else if (state.Current.JsonPropertyInfo != null) { // Nested object within an object. Debug.Assert(state.Current.IsProcessingObject(ClassType.Object)); Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo !; Debug.Assert(state.Current.IsProcessingObject(ClassType.Dictionary) || state.Current.IsProcessingObject(ClassType.Object) || state.Current.IsProcessingObject(ClassType.Enumerable)); if (state.Current.IsProcessingObject(ClassType.Dictionary)) { object?value = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.ReturnValue = value; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else if (state.Current.IsProcessingObject(ClassType.Enumerable)) { // Nested array with metadata within another array with metadata. HandleStartObjectInEnumerable(ref state, options, classInfo.Type); Debug.Assert(options.ReferenceHandling.ShouldReadPreservedReferences()); Debug.Assert(state.Current.JsonClassInfo !.Type.GetGenericTypeDefinition() == typeof(JsonPreservableArrayReference <>)); state.Current.ReturnValue = state.Current.JsonClassInfo.CreateObject !(); state.Current.IsNestedPreservedArray = true; } }
private static void HandleStartObjectInEnumerable(ref ReadStack state, JsonSerializerOptions options, Type type) { if (!state.Current.CollectionPropertyInitialized) { if (options.ReferenceHandling.ShouldReadPreservedReferences()) { HandleStartPreservedArray(ref state, options); } else { // We have bad JSON: enumerable element appeared without preceding StartArray token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(type); } } else { Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } }
private static void HandleStartPreservedArray(ref ReadStack state, JsonSerializerOptions options) { // Check we are not parsing into an immutable list or array. if (state.Current.JsonPropertyInfo !.EnumerableConverter != null) { ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(state.Current.JsonPropertyInfo.DeclaredPropertyType); } Type preservedObjType = state.Current.JsonPropertyInfo.GetJsonPreservableArrayReferenceType(); if (state.Current.IsProcessingProperty(ClassType.Enumerable)) { state.Push(); state.Current.Initialize(preservedObjType, options); } else { // For array objects, we don't need to Push a new frame to the stack, // so we just call Initialize again passing the wrapper class // since we are going to handle the array at the moment we step into JsonPreservableArrayReference<T>.Values. state.Current.Initialize(preservedObjType, options); } state.Current.IsPreservedArray = true; }
private static void HandleStartArray(JsonSerializerOptions options, ref ReadStack state) { if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } Debug.Assert(state.Current.JsonClassInfo != null); JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options); } // Verify that we are processing a valid enumerable or dictionary. if (((ClassType.Enumerable | ClassType.Dictionary) & jsonPropertyInfo.ClassType) == 0) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(jsonPropertyInfo.RuntimePropertyType); } if (state.Current.CollectionPropertyInitialized) { // An array nested in a dictionary or array, so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo !.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; // The current JsonPropertyInfo will be null if the current type is not one of // ClassType.Value | ClassType.Enumerable | ClassType.Dictionary. // We should not see ClassType.Value here because we handle it on a different code // path invoked in the main read loop. // Only ClassType.Enumerable is valid here since we just saw a StartArray token. if (state.Current.JsonPropertyInfo == null || state.Current.JsonPropertyInfo.ClassType != ClassType.Enumerable) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type); } // Set or replace the existing enumerable value. object?value = ReadStackFrame.CreateEnumerableValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.DetermineEnumerablePopulationStrategy(value); if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo !.SetValueAsObject(state.Current.ReturnValue, value); } else { state.Current.ReturnValue = value; } } }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); } // Verify that we don't have a multidimensional array. Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath); } Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary); if (state.Current.CollectionPropertyInitialized) { // A nested json array so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.JsonClassInfo.ClassType == ClassType.Value) { state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader); } else { // If current property is already set (from a constructor, for example) leave as-is. if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null) { // Create the enumerable. object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options); // If value is not null, then we don't have a converter so apply the value. if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // Primitive arrays being returned without object state.Current.SetReturnValue(value); } } } } }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); } // Verify that we have a valid enumerable. Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType); } Debug.Assert(state.Current.IsProcessingCollection()); if (state.Current.CollectionPropertyInitialized) { // A nested json array so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; // We should not be processing custom converters here (converters are of ClassType.Value). Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Value); // Set or replace the existing enumerable value. object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // Primitive arrays being returned without object state.Current.SetReturnValue(value); } } }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary()); // Note: unless we are a root object, we are going to push a property onto the ReadStack // in the if/else if check below. if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable (non-dictionary). if (!state.Current.CollectionPropertyInitialized) { // We have bad JSON: enumerable element appeared without preceding StartArray token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonPropertyInfo.DeclaredPropertyType); } Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object within an object. Debug.Assert(state.Current.IsProcessingObject(ClassType.Object)); Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingObject(ClassType.Dictionary)) { object value = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.ReturnValue = value; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else { // Only dictionaries or objects are valid given the `StartObject` token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type); } }
private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary so push new frame. if (state.Current.PropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo; state.Current.InitializeJsonPropertyInfo(); state.Current.PropertyInitialized = true; ClassType classType = state.Current.JsonClassInfo.ClassType; if (classType == ClassType.Value && jsonPropertyInfo.ElementClassInfo.Type != typeof(object) && jsonPropertyInfo.ElementClassInfo.Type != typeof(JsonElement)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.JsonPath); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject(); } else { if (classInfo.CreateObject == null) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type, reader, state.JsonPath); return; } state.Current.ReturnValue = classInfo.CreateObject(); } return; } state.Current.PropertyInitialized = true; if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair) { JsonClassInfo dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateObject(); } // Else if current property is already set (from a constructor, for example), leave as-is. else if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null) { // Create the dictionary. JsonClassInfo dictionaryClassInfo = jsonPropertyInfo.RuntimeClassInfo; IDictionary value = (IDictionary)dictionaryClassInfo.CreateObject(); if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.SetReturnValue(value); } } } }
private static void ReadCore( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack) { try { JsonReaderState initialState = default; long initialBytesConsumed = default; while (true) { if (readStack.ReadAhead) { // When we're reading ahead we always have to save the state // as we don't know if the next token is an opening object or // array brace. initialState = reader.CurrentState; initialBytesConsumed = reader.BytesConsumed; } if (!reader.Read()) { // Need more data break; } JsonTokenType tokenType = reader.TokenType; if (options.ReferenceHandling.ShouldReadPreservedReferences()) { CheckValidTokenAfterMetadataValues(ref readStack, tokenType); } if (JsonHelpers.IsInRangeInclusive(tokenType, JsonTokenType.String, JsonTokenType.False)) { Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False); HandleValue(tokenType, options, ref reader, ref readStack); } else if (tokenType == JsonTokenType.PropertyName) { HandlePropertyName(options, ref reader, ref readStack); } else if (tokenType == JsonTokenType.StartObject) { if (readStack.Current.SkipProperty) { readStack.Push(); readStack.Current.Drain = true; } else if (readStack.Current.IsProcessingValue()) { if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed)) { // Need more data break; } } else if (readStack.Current.IsProcessingDictionary()) { HandleStartDictionary(options, ref readStack); } else { HandleStartObject(options, ref readStack); } } else if (tokenType == JsonTokenType.EndObject) { if (readStack.Current.Drain) { readStack.Pop(); // Clear the current property in case it is a dictionary, since dictionaries must have EndProperty() called when completed. // A non-dictionary property can also have EndProperty() called when completed, although it is redundant. readStack.Current.EndProperty(); } else if (readStack.Current.ReferenceId != null) { Debug.Assert(options.ReferenceHandling.ShouldReadPreservedReferences()); HandleReference(ref readStack); } else if (readStack.Current.IsProcessingDictionary()) { HandleEndDictionary(options, ref readStack); } else { HandleEndObject(ref readStack); } } else if (tokenType == JsonTokenType.StartArray) { if (!readStack.Current.IsProcessingValue()) { HandleStartArray(options, ref readStack); } else if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed)) { // Need more data break; } } else if (tokenType == JsonTokenType.EndArray) { HandleEndArray(options, ref readStack); } else if (tokenType == JsonTokenType.Null) { HandleNull(options, ref reader, ref readStack); } } } catch (JsonReaderException ex) { // Re-throw with Path information. ThrowHelper.ReThrowWithPath(readStack, ex); } catch (FormatException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException) { ThrowHelper.ReThrowWithPath(readStack, reader, ex); } catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException) { ThrowHelper.ReThrowWithPath(readStack, reader, ex); } catch (JsonException ex) { ThrowHelper.AddExceptionInformation(readStack, reader, ex); throw; } readStack.BytesConsumed += reader.BytesConsumed; }
private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable()); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary so push new frame. if (state.Current.CollectionPropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo; state.Current.InitializeJsonPropertyInfo(); state.Current.CollectionPropertyInitialized = true; ClassType classType = state.Current.JsonClassInfo.ClassType; if (classType == ClassType.Value) { Type elementClassInfoType = jsonPropertyInfo.ElementClassInfo.Type; if (elementClassInfoType != typeof(object) && elementClassInfoType != typeof(JsonElement)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type); } } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingIDictionaryConstructible()) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateConcreteDictionary(); } else { if (classInfo.CreateObject == null) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type); return; } state.Current.ReturnValue = classInfo.CreateObject(); } return; } state.Current.CollectionPropertyInitialized = true; if (state.Current.IsProcessingIDictionaryConstructible()) { JsonClassInfo dictionaryClassInfo; if (jsonPropertyInfo.DeclaredPropertyType == jsonPropertyInfo.ImplementedPropertyType) { dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); } else { dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.DeclaredPropertyType); } state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateConcreteDictionary(); } else { // Create the dictionary. JsonClassInfo dictionaryClassInfo = jsonPropertyInfo.RuntimeClassInfo; IDictionary value = (IDictionary)dictionaryClassInfo.CreateObject(); if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.SetReturnValue(value); } } } }
private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable()); JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo !.CreateRootProperty(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary, so push new frame. if (state.Current.CollectionPropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo !; state.Current.InitializeJsonPropertyInfo(); JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingDictionary()) { object?dictValue = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (dictValue != null) { state.Current.ReturnValue = dictValue; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type); } return; } state.Current.CollectionPropertyInitialized = true; object?value = ReadStackFrame.CreateDictionaryValue(ref state); if (value != null) { state.Current.DetermineIfDictionaryCanBePopulated(value); if (state.Current.ReturnValue != null) { Debug.Assert(state.Current.JsonPropertyInfo != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.ReturnValue = value; } } }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionaryOrIDictionaryConstructible()); // Note: unless we are a root object, we are going to push a property onto the ReadStack // in the if/else if check below. if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable (non-dictionary). if (!state.Current.CollectionPropertyInitialized) { // We have bad JSON: enumerable element appeared without preceding StartArray token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonPropertyInfo.DeclaredPropertyType); } Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object within an object. Debug.Assert(state.Current.IsProcessingObject(ClassType.Object)); Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingObject(ClassType.IDictionaryConstructible)) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateConcreteDictionary(); state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Dictionary)) { if (classInfo.CreateObject == null) { throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(classInfo.Type, parentType: null, memberInfo: null); } state.Current.ReturnValue = classInfo.CreateObject(); state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); if (state.Current.IsProcessingDictionary()) { state.Current.CollectionPropertyInitialized = true; } } else { // Only dictionaries or objects are valid given the `StartObject` token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type); } }
private static void ReadCore( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack) { try { JsonReaderState initialState = default; long initialBytesConsumed = default; while (true) { if (readStack.ReadAhead) { // When we're reading ahead we always have to save the state // as we don't know if the next token is an opening object or // array brace. initialState = reader.CurrentState; initialBytesConsumed = reader.BytesConsumed; } if (!reader.Read()) { // Need more data break; } JsonTokenType tokenType = reader.TokenType; if (JsonHelpers.IsInRangeInclusive(tokenType, JsonTokenType.String, JsonTokenType.False)) { Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False); HandleValue(tokenType, options, ref reader, ref readStack); } else if (tokenType == JsonTokenType.PropertyName) { HandlePropertyName(options, ref reader, ref readStack); } else if (tokenType == JsonTokenType.StartObject) { if (readStack.Current.SkipProperty) { readStack.Push(); readStack.Current.Drain = true; } else if (readStack.Current.IsProcessingValue(tokenType)) { if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed)) { // Need more data break; } } else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible) { HandleStartDictionary(options, ref reader, ref readStack); } else { HandleStartObject(options, ref reader, ref readStack); } } else if (tokenType == JsonTokenType.EndObject) { if (readStack.Current.Drain) { readStack.Pop(); } else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible) { HandleEndDictionary(options, ref reader, ref readStack); } else { HandleEndObject(options, ref reader, ref readStack); } } else if (tokenType == JsonTokenType.StartArray) { if (!readStack.Current.IsProcessingValue(tokenType)) { HandleStartArray(options, ref reader, ref readStack); } else if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed)) { // Need more data break; } } else if (tokenType == JsonTokenType.EndArray) { HandleEndArray(options, ref reader, ref readStack); } else if (tokenType == JsonTokenType.Null) { HandleNull(ref reader, ref readStack, options); } } } catch (JsonReaderException ex) { // Re-throw with Path information. ThrowHelper.ReThrowWithPath(readStack, ex); } catch (FormatException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException) { ThrowHelper.ReThrowWithPath(readStack, reader, ex); } catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException) { ThrowHelper.ReThrowWithPath(readStack, reader, ex); } catch (JsonException ex) { ThrowHelper.AddExceptionInformation(readStack, reader, ex); throw; } readStack.BytesConsumed += reader.BytesConsumed; return; }
private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable()); JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo !.CreateRootProperty(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary, so push new frame. if (state.Current.CollectionPropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo !; state.Current.InitializeJsonPropertyInfo(); JsonClassInfo classInfo = state.Current.JsonClassInfo; Debug.Assert(state.Current.IsProcessingDictionary() || state.Current.IsProcessingObject(ClassType.Object) || state.Current.IsProcessingObject(ClassType.Enumerable)); if (state.Current.IsProcessingDictionary()) { object?dictValue = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (dictValue != null) { state.Current.ReturnValue = dictValue; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else if (state.Current.IsProcessingObject(ClassType.Enumerable)) { // Array with metadata within the dictionary. HandleStartObjectInEnumerable(ref state, options, classInfo.Type); Debug.Assert(options.ReferenceHandling.ShouldReadPreservedReferences()); Debug.Assert(state.Current.JsonClassInfo !.Type.GetGenericTypeDefinition() == typeof(JsonPreservableArrayReference <>)); state.Current.ReturnValue = state.Current.JsonClassInfo.CreateObject !(); } return; } state.Current.CollectionPropertyInitialized = true; object?value = ReadStackFrame.CreateDictionaryValue(ref state); if (value != null) { state.Current.DetermineIfDictionaryCanBePopulated(value); if (state.Current.ReturnValue != null) { Debug.Assert(state.Current.JsonPropertyInfo != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.ReturnValue = value; } } }