private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, 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); } } state.Current.ReturnValue = classInfo.CreateObject(); }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { if (state.Current.Skip()) { state.Push(); state.Current.Drain = true; return; } if (state.Current.IsEnumerable() || state.Current.IsPropertyEnumerable()) { // An array of objects either on the current property or on a list Type objType = state.Current.GetElementType(); state.Push(); state.Current.JsonClassInfo = options.GetOrAddClass(objType); } else if (state.Current.JsonPropertyInfo != null) { // Nested object Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.JsonClassInfo = options.GetOrAddClass(objType); } JsonClassInfo classInfo = state.Current.JsonClassInfo; state.Current.ReturnValue = classInfo.CreateObject(); }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.Skip()) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } Type arrayType = state.Current.JsonPropertyInfo.PropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1)) { ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(arrayType, reader, state); } Debug.Assert(state.Current.IsPropertyEnumerable()); if (state.Current.IsPropertyEnumerable()) { if (state.Current.EnumerableCreated) { // A nested json array so push a new stack frame. Type elementType = state.Current.JsonClassInfo.ElementClassInfo.GetPolicyProperty().PropertyType; state.Push(); state.Current.JsonClassInfo = options.GetOrAddClass(elementType); state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetPolicyProperty(); state.Current.PopStackOnEndArray = true; } else { state.Current.EnumerableCreated = true; } // If current property is already set (from a constructor, for example) leave as-is if (state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue, options) == null) { // Create the enumerable. object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options); if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value, options); } else { // Primitive arrays being returned without object state.Current.SetReturnValue(value, options); } } } } }
private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.Skip()) { state.Push(); state.Current.Drain = true; return; } if (state.Current.IsProcessingEnumerable) { Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { if (state.Current.IsDictionary) { // Verify that the Dictionary can be deserialized by having <string> as first generic argument. Debug.Assert(state.Current.JsonClassInfo.Type.GetGenericArguments().Length >= 1); if (state.Current.JsonClassInfo.Type.GetGenericArguments()[0].UnderlyingSystemType != typeof(string)) { ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state); } ClassType classType = state.Current.JsonClassInfo.ElementClassInfo.ClassType; if (state.Current.ReturnValue == null) { // The Dictionary created below will be returned to corresponding Parse() etc method. // Ensure any nested array creates a new frame. state.Current.EnumerableCreated = true; } else { Debug.Assert(classType == ClassType.Object || classType == ClassType.Dictionary); // A nested object or dictionary. JsonClassInfo classInfoTemp = state.Current.JsonClassInfo; state.Push(); state.Current.JsonClassInfo = classInfoTemp.ElementClassInfo; state.Current.InitializeJsonPropertyInfo(); } } else { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } } JsonClassInfo classInfo = state.Current.JsonClassInfo; state.Current.ReturnValue = classInfo.CreateObject(); }
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) { Debug.Assert(state.Current.IsDictionary); JsonClassInfo classInfoTemp = state.Current.JsonClassInfo; state.Push(); state.Current.JsonClassInfo = classInfoTemp.ElementClassInfo; state.Current.InitializeJsonPropertyInfo(); 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.PropertyPath); } JsonClassInfo classInfo = state.Current.JsonClassInfo; state.Current.ReturnValue = classInfo.CreateObject(); return; } state.Current.PropertyInitialized = true; // If current property is already set (from a constructor, for example) leave as-is. if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null) { // Create the dictionary. JsonClassInfo dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); 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 HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, 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; state.Current.ReturnValue = classInfo.CreateObject(); }
internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T?value) { if (ConverterStrategy == ConverterStrategy.Value) { // A value converter should never be within a continuation. Debug.Assert(!state.IsContinuation); // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; return(true); } #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { if (state.Current.NumberHandling != null) { value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options); } else { value = Read(ref reader, typeToConvert, options); } } else #endif { JsonTokenType originalPropertyTokenType = reader.TokenType; int originalPropertyDepth = reader.CurrentDepth; long originalPropertyBytesConsumed = reader.BytesConsumed; if (state.Current.NumberHandling != null) { value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options); } else { value = Read(ref reader, typeToConvert, options); } VerifyRead( originalPropertyTokenType, originalPropertyDepth, originalPropertyBytesConsumed, isValueConverter: true, ref reader); } if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve && CanBePolymorphic && value is JsonElement element) { // Edge case where we want to lookup for a reference when parsing into typeof(object) // instead of return `value` as a JsonElement. Debug.Assert(TypeToConvert == typeof(object)); if (JsonSerializer.TryGetReferenceFromJsonElement(ref state, element, out object?referenceValue)) { value = (T?)referenceValue; } } return(true); } bool success; // Remember if we were a continuation here since Push() may affect IsContinuation. bool wasContinuation = state.IsContinuation; #if DEBUG // DEBUG: ensure push/pop operations preserve stack integrity JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; #endif state.Push(); Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type)); #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead && !wasContinuation) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } // For perf and converter simplicity, handle null here instead of forwarding to the converter. value = default; success = true; } else { success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); } } else #endif { if (!wasContinuation) { // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; state.Pop(true); #if DEBUG Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); #endif return(true); } Debug.Assert(state.Current.OriginalTokenType == JsonTokenType.None); state.Current.OriginalTokenType = reader.TokenType; Debug.Assert(state.Current.OriginalDepth == 0); state.Current.OriginalDepth = reader.CurrentDepth; } success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); if (success) { if (state.IsContinuation) { // The resumable converter did not forward to the next converter that previously returned false. ThrowHelper.ThrowJsonException_SerializationConverterRead(this); } VerifyRead( state.Current.OriginalTokenType, state.Current.OriginalDepth, bytesConsumed: 0, isValueConverter: false, ref reader); // No need to clear state.Current.* since a stack pop will occur. } } state.Pop(success); #if DEBUG Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); #endif return(success); }
internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNull] out T value) { if (ClassType == ClassType.Value) { // A value converter should never be within a continuation. Debug.Assert(!state.IsContinuation); // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNull) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; return(true); } #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { value = Read(ref reader, typeToConvert, options); } else #endif { JsonTokenType originalPropertyTokenType = reader.TokenType; int originalPropertyDepth = reader.CurrentDepth; long originalPropertyBytesConsumed = reader.BytesConsumed; value = Read(ref reader, typeToConvert, options); VerifyRead( originalPropertyTokenType, originalPropertyDepth, originalPropertyBytesConsumed, isValueConverter: true, ref reader); } return(true); } bool success; // Remember if we were a continuation here since Push() may affect IsContinuation. bool wasContinuation = state.IsContinuation; state.Push(); #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { if (reader.TokenType == JsonTokenType.Null && !HandleNull && !wasContinuation) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } // For perf and converter simplicity, handle null here instead of forwarding to the converter. value = default; success = true; } else { success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); } } else #endif { if (!wasContinuation) { // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNull) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; state.Pop(true); return(true); } Debug.Assert(state.Current.OriginalTokenType == JsonTokenType.None); state.Current.OriginalTokenType = reader.TokenType; Debug.Assert(state.Current.OriginalDepth == 0); state.Current.OriginalDepth = reader.CurrentDepth; } success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); if (success) { if (state.IsContinuation) { // The resumable converter did not forward to the next converter that previously returned false. ThrowHelper.ThrowJsonException_SerializationConverterRead(this); } VerifyRead( state.Current.OriginalTokenType, state.Current.OriginalDepth, bytesConsumed: 0, isValueConverter: false, ref reader); // No need to clear state.Current.* since a stack pop will occur. } } state.Pop(success); return(success); }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { JsonPropertyInfo jsonPropertyInfo; jsonPropertyInfo = state.Current.JsonPropertyInfo; bool skip = jsonPropertyInfo != null && !jsonPropertyInfo.ShouldDeserialize; if (skip || state.Current.Skip()) { // 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); } Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1)) { ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(arrayType, reader, state); } Debug.Assert(state.Current.IsPropertyEnumerable || state.Current.IsDictionary); if (state.Current.EnumerableCreated) { // A nested json array so push a new stack frame. Type elementType = state.Current.JsonClassInfo.ElementClassInfo.GetPolicyProperty().RuntimePropertyType; state.Push(); state.Current.Initialize(elementType, options); state.Current.PopStackOnEnd = true; } else { state.Current.EnumerableCreated = true; } jsonPropertyInfo = state.Current.JsonPropertyInfo; // 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 != 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); } } } }
internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T?value) { // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead && !state.IsContinuation) { if (default(T) is not null) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; return(true); } if (ConverterStrategy == ConverterStrategy.Value) { // A value converter should never be within a continuation. Debug.Assert(!state.IsContinuation); #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { if (state.Current.NumberHandling != null && IsInternalConverterForNumberType) { value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options); } else { value = Read(ref reader, typeToConvert, options); } } else #endif { JsonTokenType originalPropertyTokenType = reader.TokenType; int originalPropertyDepth = reader.CurrentDepth; long originalPropertyBytesConsumed = reader.BytesConsumed; if (state.Current.NumberHandling != null && IsInternalConverterForNumberType) { value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options); } else { value = Read(ref reader, typeToConvert, options); } VerifyRead( originalPropertyTokenType, originalPropertyDepth, originalPropertyBytesConsumed, isValueConverter: true, ref reader); } return(true); } Debug.Assert(IsInternalConverter); bool isContinuation = state.IsContinuation; bool success; #if DEBUG // DEBUG: ensure push/pop operations preserve stack integrity JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; #endif state.Push(); Debug.Assert(TypeToConvert == state.Current.JsonTypeInfo.Type); #if DEBUG // For performance, only perform validation on internal converters on debug builds. if (!isContinuation) { Debug.Assert(state.Current.OriginalTokenType == JsonTokenType.None); state.Current.OriginalTokenType = reader.TokenType; Debug.Assert(state.Current.OriginalDepth == 0); state.Current.OriginalDepth = reader.CurrentDepth; } #endif success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); #if DEBUG if (success) { if (state.IsContinuation) { // The resumable converter did not forward to the next converter that previously returned false. ThrowHelper.ThrowJsonException_SerializationConverterRead(this); } VerifyRead( state.Current.OriginalTokenType, state.Current.OriginalDepth, bytesConsumed: 0, isValueConverter: false, ref reader); // No need to clear state.Current.* since a stack pop will occur. } #endif state.Pop(success); #if DEBUG Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); #endif return(success); }
private static void ReadCore( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack) { try { JsonReaderState initialState = default; while (true) { if (options.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; } 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) { if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState)) { // Need more data break; } } else if (readStack.Current.IsProcessingDictionary) { 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) { HandleEndDictionary(options, ref reader, ref readStack); } else { HandleEndObject(options, ref reader, ref readStack); } } else if (tokenType == JsonTokenType.StartArray) { if (!readStack.Current.IsProcessingValue) { HandleStartArray(options, ref reader, ref readStack); } else if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState)) { // 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 e) { // Re-throw with Path information. ThrowHelper.ReThrowWithPath(e, readStack.JsonPath); } readStack.BytesConsumed += reader.BytesConsumed; return; }
private static void ReadCore( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { try { while (reader.Read()) { 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); if (HandleValue(tokenType, options, ref reader, ref state)) { continue; } } else if (tokenType == JsonTokenType.PropertyName) { HandlePropertyName(options, ref reader, ref state); } else if (tokenType == JsonTokenType.StartObject) { if (state.Current.SkipProperty) { state.Push(); state.Current.Drain = true; } else if (state.Current.IsProcessingValue) { if (HandleValue(tokenType, options, ref reader, ref state)) { continue; } } else if (state.Current.IsProcessingDictionary) { HandleStartDictionary(options, ref reader, ref state); } else { HandleStartObject(options, ref reader, ref state); } } else if (tokenType == JsonTokenType.EndObject) { if (state.Current.Drain) { state.Pop(); } else if (state.Current.IsProcessingDictionary) { HandleEndDictionary(options, ref reader, ref state); } else { HandleEndObject(options, ref reader, ref state); } } else if (tokenType == JsonTokenType.StartArray) { if (!state.Current.IsProcessingValue) { HandleStartArray(options, ref reader, ref state); } else if (HandleValue(tokenType, options, ref reader, ref state)) { continue; } } else if (tokenType == JsonTokenType.EndArray) { if (HandleEndArray(options, ref reader, ref state)) { continue; } } else if (tokenType == JsonTokenType.Null) { if (HandleNull(ref reader, ref state, options)) { continue; } } } } catch (JsonReaderException e) { // Re-throw with Path information. ThrowHelper.ReThrowWithPath(e, state.PropertyPath); } return; }