private static bool HandleEndObject(JsonSerializerOptions options, ref ReadStack state) { bool isLastFrame = state.IsLastFrame; if (state.Current.Drain) { state.Pop(); return(isLastFrame); } state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current); object value = state.Current.ReturnValue; if (isLastFrame) { state.Current.Reset(); state.Current.ReturnValue = value; return(true); } state.Pop(); ApplyObjectToEnumerable(value, options, ref state.Current); return(false); }
private static void HandleEndObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary); state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current); object value = state.Current.ReturnValue; if (state.IsLastFrame) { state.Current.Reset(); state.Current.ReturnValue = value; } else { state.Pop(); ApplyObjectToEnumerable(value, ref state, ref reader); } }
private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.IsDictionaryProperty) { // We added the items to the dictionary already. state.Current.ResetProperty(); } else if (state.Current.IsImmutableDictionaryProperty) { Debug.Assert(state.Current.TempDictionaryValues != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, CreateImmutableDictionaryFromTempValues(ref state, options)); state.Current.ResetProperty(); } else { object value; if (state.Current.TempDictionaryValues != null) { value = CreateImmutableDictionaryFromTempValues(ref state, options); } else { value = state.Current.ReturnValue; } if (state.IsLastFrame) { // Set the return value directly since this will be returned to the user. state.Current.Reset(); state.Current.ReturnValue = value; } else { state.Pop(); ApplyObjectToEnumerable(value, ref state, ref reader); } } }
private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.IsDictionaryProperty) { // We added the items to the dictionary already. state.Current.ResetProperty(); } else { object value = state.Current.ReturnValue; if (state.IsLastFrame) { // Set the return value directly since this will be returned to the user. state.Current.Reset(); state.Current.ReturnValue = value; } else { state.Pop(); ApplyObjectToEnumerable(value, ref state, ref reader); } } }
private static bool HandleEndArray( JsonSerializerOptions options, ref ReadStack state) { bool lastFrame = state.IsLastFrame; if (state.Current.Drain) { // The array is not being applied to the object. state.Pop(); return(lastFrame); } IEnumerable value = ReadStackFrame.GetEnumerableValue(state.Current); if (value == null) { // We added the items to the list property already. state.Current.ResetProperty(); return(false); } bool setPropertyDirectly; if (state.Current.TempEnumerableValues != null) { JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter; Debug.Assert(converter != null); Type elementType = state.Current.GetElementType(); value = converter.CreateFromList(elementType, (IList)value); setPropertyDirectly = true; } else { setPropertyDirectly = false; } bool valueReturning = state.Current.PopStackOnEndArray; if (state.Current.PopStackOnEndArray) { state.Pop(); } if (lastFrame) { if (state.Current.ReturnValue == null) { // Returning a converted list or object. state.Current.Reset(); state.Current.ReturnValue = value; return(true); } else if (state.Current.IsEnumerable()) { // Returning a non-converted list. return(true); } // else there must be an outer object, so we'll return false here. } ApplyObjectToEnumerable(value, options, ref state.Current, setPropertyDirectly: setPropertyDirectly); if (!valueReturning) { state.Current.ResetProperty(); } return(false); }
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); }
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 bool HandleEndArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { bool lastFrame = state.IsLastFrame; if (state.Current.Drain) { // The array is not being applied to the object. state.Pop(); return(lastFrame); } IEnumerable value = ReadStackFrame.GetEnumerableValue(state.Current); if (state.Current.TempEnumerableValues != null) { // We have a converter; possibilities: // - Add value to current frame's current property or TempEnumerableValues. // - Add value to previous frame's current property or TempEnumerableValues. // - Set current property on current frame to value. // - Set current property on previous frame to value. // - Set ReturnValue if root frame and value is the actual return value. JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter; Debug.Assert(converter != null); value = converter.CreateFromList(ref state, (IList)value, options); state.Current.TempEnumerableValues = null; } else if (state.Current.IsEnumerableProperty) { // We added the items to the list already. state.Current.ResetProperty(); return(false); } if (lastFrame) { if (state.Current.ReturnValue == null) { // Returning a converted list or object. state.Current.Reset(); state.Current.ReturnValue = value; return(true); } else if (state.Current.IsEnumerable || state.Current.IsDictionary) { // Returning a non-converted list. return(true); } // else there must be an outer object, so we'll return false here. } else if (state.Current.IsEnumerable) { state.Pop(); } ApplyObjectToEnumerable(value, ref state, ref reader); return(false); }
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; }