public override int Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.String) { ReadOnlySpan <byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed) { return(number); } if (int.TryParse(reader.GetString(), out number)) { return(number); } } return(reader.GetInt32()); }
public static Dictionary <string, object> ReaderDictionaryLoop(ref Utf8JsonReader json) { Dictionary <string, object> dictionary = new Dictionary <string, object>(); string key = ""; object value = null; while (json.Read()) { JsonTokenType tokenType = json.TokenType; ReadOnlySpan <byte> valueSpan = json.ValueSpan; switch (tokenType) { case JsonTokenType.PropertyName: key = json.GetString(); dictionary.Add(key, null); break; case JsonTokenType.True: case JsonTokenType.False: value = valueSpan[0] == 't'; if (dictionary.TryGetValue(key, out _)) { dictionary[key] = value; } else { dictionary.Add(key, value); } break; case JsonTokenType.Number: json.TryGetDouble(out double valueDouble); if (dictionary.TryGetValue(key, out _)) { dictionary[key] = valueDouble; } else { dictionary.Add(key, valueDouble); } break; case JsonTokenType.String: string valueString = json.GetString(); if (dictionary.TryGetValue(key, out _)) { dictionary[key] = valueString; } else { dictionary.Add(key, valueString); } break; case JsonTokenType.Null: value = null; if (dictionary.TryGetValue(key, out _)) { dictionary[key] = value; } else { dictionary.Add(key, value); } break; case JsonTokenType.StartObject: Assert.True(valueSpan.SequenceEqual(new byte[] { (byte)'{' })); value = ReaderDictionaryLoop(ref json); if (dictionary.TryGetValue(key, out _)) { dictionary[key] = value; } else { dictionary.Add(key, value); } break; case JsonTokenType.StartArray: Assert.True(valueSpan.SequenceEqual(new byte[] { (byte)'[' })); value = ReaderListLoop(ref json); if (dictionary.TryGetValue(key, out _)) { dictionary[key] = value; } else { dictionary.Add(key, value); } break; case JsonTokenType.EndObject: Assert.True(valueSpan.SequenceEqual(new byte[] { (byte)'}' })); return(dictionary); case JsonTokenType.None: case JsonTokenType.Comment: default: break; } } return(dictionary); }
public static byte[] ReaderLoop(int inpuDataLength, out int length, ref Utf8JsonReader json) { byte[] outputArray = new byte[inpuDataLength]; Span <byte> destination = outputArray; while (json.Read()) { JsonTokenType tokenType = json.TokenType; ReadOnlySpan <byte> valueSpan = json.HasValueSequence ? json.ValueSequence.ToArray() : json.ValueSpan; if (json.HasValueSequence) { Assert.True(json.ValueSpan == default); if ((tokenType != JsonTokenType.String && tokenType != JsonTokenType.PropertyName) || json.GetString().Length != 0) { // Empty strings could still make this true, i.e. "" Assert.False(json.ValueSequence.IsEmpty); } } else { Assert.True(json.ValueSequence.IsEmpty); if ((tokenType != JsonTokenType.String && tokenType != JsonTokenType.PropertyName) || json.GetString().Length != 0) { // Empty strings could still make this true, i.e. "" Assert.False(json.ValueSpan == default); } } switch (tokenType) { case JsonTokenType.PropertyName: valueSpan.CopyTo(destination); destination[valueSpan.Length] = (byte)','; destination[valueSpan.Length + 1] = (byte)' '; destination = destination.Slice(valueSpan.Length + 2); break; case JsonTokenType.Number: case JsonTokenType.String: case JsonTokenType.Comment: valueSpan.CopyTo(destination); destination[valueSpan.Length] = (byte)','; destination[valueSpan.Length + 1] = (byte)' '; destination = destination.Slice(valueSpan.Length + 2); break; case JsonTokenType.True: // Special casing True/False so that the casing matches with Json.NET destination[0] = (byte)'T'; destination[1] = (byte)'r'; destination[2] = (byte)'u'; destination[3] = (byte)'e'; destination[valueSpan.Length] = (byte)','; destination[valueSpan.Length + 1] = (byte)' '; destination = destination.Slice(valueSpan.Length + 2); break; case JsonTokenType.False: destination[0] = (byte)'F'; destination[1] = (byte)'a'; destination[2] = (byte)'l'; destination[3] = (byte)'s'; destination[4] = (byte)'e'; destination[valueSpan.Length] = (byte)','; destination[valueSpan.Length + 1] = (byte)' '; destination = destination.Slice(valueSpan.Length + 2); break; case JsonTokenType.Null: // Special casing Null so that it matches what JSON.NET does break; case JsonTokenType.StartObject: Assert.True(json.ValueSpan.SequenceEqual(new byte[] { (byte)'{' })); Assert.True(json.ValueSequence.IsEmpty); break; case JsonTokenType.EndObject: Assert.True(json.ValueSpan.SequenceEqual(new byte[] { (byte)'}' })); Assert.True(json.ValueSequence.IsEmpty); break; case JsonTokenType.StartArray: Assert.True(json.ValueSpan.SequenceEqual(new byte[] { (byte)'[' })); Assert.True(json.ValueSequence.IsEmpty); break; case JsonTokenType.EndArray: Assert.True(json.ValueSpan.SequenceEqual(new byte[] { (byte)']' })); Assert.True(json.ValueSequence.IsEmpty); break; default: break; } } length = outputArray.Length - destination.Length; return(outputArray); }
private static void HandlePropertyName( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.Drain) { return; } Debug.Assert(state.Current.ReturnValue != null || state.Current.TempDictionaryValues != null); Debug.Assert(state.Current.JsonClassInfo != null); bool isProcessingDictObject = state.Current.IsProcessingObject(ClassType.Dictionary); if ((isProcessingDictObject || state.Current.IsProcessingProperty(ClassType.Dictionary)) && state.Current.JsonClassInfo.DataExtensionProperty != state.Current.JsonPropertyInfo) { if (isProcessingDictObject) { state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty; } state.Current.KeyName = reader.GetString(); } else { Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object); state.Current.EndProperty(); ReadOnlySpan <byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; if (reader._stringHasEscaping) { int idx = propertyName.IndexOf(JsonConstants.BackSlash); Debug.Assert(idx != -1); propertyName = GetUnescapedString(propertyName, idx); } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(propertyName, ref state.Current); if (jsonPropertyInfo == JsonPropertyInfo.s_missingProperty) { JsonPropertyInfo dataExtProperty = state.Current.JsonClassInfo.DataExtensionProperty; if (dataExtProperty == null) { state.Current.JsonPropertyInfo = JsonPropertyInfo.s_missingProperty; } else { state.Current.JsonPropertyInfo = dataExtProperty; state.Current.JsonPropertyName = propertyName.ToArray(); state.Current.KeyName = JsonHelpers.Utf8GetString(propertyName); state.Current.CollectionPropertyInitialized = true; CreateDataExtensionProperty(dataExtProperty, ref state); } } else { // Support JsonException.Path. Debug.Assert( jsonPropertyInfo.JsonPropertyName == null || options.PropertyNameCaseInsensitive || propertyName.SequenceEqual(jsonPropertyInfo.JsonPropertyName)); state.Current.JsonPropertyInfo = jsonPropertyInfo; if (jsonPropertyInfo.JsonPropertyName == null) { byte[] propertyNameArray = propertyName.ToArray(); if (options.PropertyNameCaseInsensitive) { // Each payload can have a different name here; remember the value on the temporary stack. state.Current.JsonPropertyName = propertyNameArray; } else { // Prevent future allocs by caching globally on the JsonPropertyInfo which is specific to a Type+PropertyName // so it will match the incoming payload except when case insensitivity is enabled (which is handled above). state.Current.JsonPropertyInfo.JsonPropertyName = propertyNameArray; } } } // Increment the PropertyIndex so JsonClassInfo.GetProperty() starts with the next property. state.Current.PropertyIndex++; } }
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.TokenType != JsonTokenType.String ? throw new JsonException() : TimeSpan.ParseExact(reader.GetString(), "c", CultureInfo.InvariantCulture);
internal static bool TryReadMetadata(JsonConverter converter, JsonTypeInfo jsonTypeInfo, ref Utf8JsonReader reader, scoped ref ReadStack state) { Debug.Assert(state.Current.ObjectState == StackFrameObjectState.StartToken); Debug.Assert(state.Current.CanContainMetadata); while (true) { if (state.Current.PropertyState == StackFramePropertyState.None) { state.Current.PropertyState = StackFramePropertyState.ReadName; // Read the property name. if (!reader.Read()) { return(false); } } if (state.Current.PropertyState < StackFramePropertyState.Name) { if (reader.TokenType == JsonTokenType.EndObject) { // Read the entire object while parsing for metadata. return(true); } // We just read a property. The only valid next tokens are EndObject and PropertyName. Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Ref)) { // No properties whatsoever should follow a $ref property. ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state); } ReadOnlySpan <byte> propertyName = reader.GetSpan(); switch (state.Current.LatestMetadataPropertyName = GetMetadataPropertyName(propertyName, jsonTypeInfo.PolymorphicTypeResolver)) { case MetadataPropertyName.Id: state.Current.JsonPropertyName = s_idPropertyName; if (state.ReferenceResolver is null) { // Found an $id property in a type that doesn't support reference preservation ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(propertyName, ref state); } if ((state.Current.MetadataPropertyNames & (MetadataPropertyName.Id | MetadataPropertyName.Ref)) != 0) { // No $id or $ref properties should precede $id properties. ThrowHelper.ThrowJsonException_MetadataIdIsNotFirstProperty(propertyName, ref state); } if (!converter.CanHaveMetadata) { // Should not be permitted unless the converter is capable of handling metadata. ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert); } break; case MetadataPropertyName.Ref: state.Current.JsonPropertyName = s_refPropertyName; if (state.ReferenceResolver is null) { // Found a $ref property in a type that doesn't support reference preservation ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(propertyName, ref state); } if (converter.IsValueType) { // Should not be permitted if the converter is a struct. ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert); } if (state.Current.MetadataPropertyNames != 0) { // No metadata properties should precede a $ref property. ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state); } break; case MetadataPropertyName.Type: state.Current.JsonPropertyName = jsonTypeInfo.PolymorphicTypeResolver?.TypeDiscriminatorPropertyNameUtf8 ?? s_typePropertyName; if (jsonTypeInfo.PolymorphicTypeResolver is null) { // Found a $type property in a type that doesn't support polymorphism ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(propertyName, ref state); } if (state.PolymorphicTypeDiscriminator != null) { ThrowHelper.ThrowJsonException_MetadataDuplicateTypeProperty(); } break; case MetadataPropertyName.Values: state.Current.JsonPropertyName = s_valuesPropertyName; if (state.Current.MetadataPropertyNames == MetadataPropertyName.None) { // Cannot have a $values property unless there are preceding metadata properties. ThrowHelper.ThrowJsonException_MetadataStandaloneValuesProperty(ref state, propertyName); } break; default: Debug.Assert(state.Current.LatestMetadataPropertyName == MetadataPropertyName.None); // Encountered a non-metadata property, exit the reader. return(true); } state.Current.PropertyState = StackFramePropertyState.Name; } if (state.Current.PropertyState < StackFramePropertyState.ReadValue) { state.Current.PropertyState = StackFramePropertyState.ReadValue; // Read the property value. if (!reader.Read()) { return(false); } } Debug.Assert(state.Current.PropertyState == StackFramePropertyState.ReadValue); switch (state.Current.LatestMetadataPropertyName) { case MetadataPropertyName.Id: if (reader.TokenType != JsonTokenType.String) { ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); } if (state.ReferenceId != null) { ThrowHelper.ThrowNotSupportedException_ObjectWithParameterizedCtorRefMetadataNotSupported(s_refPropertyName, ref reader, ref state); } state.ReferenceId = reader.GetString(); break; case MetadataPropertyName.Ref: if (reader.TokenType != JsonTokenType.String) { ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); } if (state.ReferenceId != null) { ThrowHelper.ThrowNotSupportedException_ObjectWithParameterizedCtorRefMetadataNotSupported(s_refPropertyName, ref reader, ref state); } state.ReferenceId = reader.GetString(); break; case MetadataPropertyName.Type: Debug.Assert(state.PolymorphicTypeDiscriminator == null); switch (reader.TokenType) { case JsonTokenType.String: state.PolymorphicTypeDiscriminator = reader.GetString(); break; case JsonTokenType.Number: state.PolymorphicTypeDiscriminator = reader.GetInt32(); break; default: ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); break; } break; case MetadataPropertyName.Values: if (reader.TokenType != JsonTokenType.StartArray) { ThrowHelper.ThrowJsonException_MetadataValuesInvalidToken(reader.TokenType); } state.Current.PropertyState = StackFramePropertyState.None; state.Current.MetadataPropertyNames |= state.Current.LatestMetadataPropertyName; return(true); // "$values" property contains the nested payload, exit the metadata reader now. default: Debug.Fail("Non-metadata properties should not reach this stage."); break; } state.Current.MetadataPropertyNames |= state.Current.LatestMetadataPropertyName; state.Current.PropertyState = StackFramePropertyState.None; state.Current.JsonPropertyName = null; } }
/// <summary> /// Parses a string representing JSON document into <see cref="JsonNode"/>. /// </summary> /// <param name="json">JSON to parse.</param> /// <param name="options">Options to control the parsing behavior.</param> /// <returns><see cref="JsonNode"/> representation of <paramref name="json"/>.</returns> public static JsonNode Parse(string json, JsonNodeOptions options = default) { Utf8JsonReader reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json), options.GetReaderOptions()); var currentNodes = new Stack <KeyValuePair <string, JsonNode> >(); // nodes currently being created JsonNode toReturn = null; while (reader.Read()) { JsonTokenType tokenType = reader.TokenType; currentNodes.TryPeek(out KeyValuePair <string, JsonNode> currentPair); void AddNewPair(JsonNode jsonNode, bool keepInCurrentNodes = false) { KeyValuePair <string, JsonNode> newProperty; if (currentPair.Value == null) { // If previous token was property name, // it was added to stack with not null name and null value, // otherwise, this is first JsonNode added if (currentPair.Key != null) { // Create as property, keep name, replace null with new JsonNode: currentNodes.Pop(); newProperty = new KeyValuePair <string, JsonNode>(currentPair.Key, jsonNode); } else { // Add first JsonNode: newProperty = new KeyValuePair <string, JsonNode>(null, jsonNode); } } else { // Create as value: newProperty = new KeyValuePair <string, JsonNode>(null, jsonNode); } if (keepInCurrentNodes) { // If after adding property, it should be kept in currentNodes, it must be JsonObject or JsonArray Debug.Assert(jsonNode.ValueKind == JsonValueKind.Object || jsonNode.ValueKind == JsonValueKind.Array); currentNodes.Push(newProperty); } else { AddToParent(newProperty, ref currentNodes, ref toReturn, options.DuplicatePropertyNameHandling); } } switch (tokenType) { case JsonTokenType.StartObject: AddNewPair(new JsonObject(), true); break; case JsonTokenType.EndObject: Debug.Assert(currentPair.Value is JsonObject); currentNodes.Pop(); AddToParent(currentPair, ref currentNodes, ref toReturn, options.DuplicatePropertyNameHandling); break; case JsonTokenType.StartArray: AddNewPair(new JsonArray(), true); break; case JsonTokenType.EndArray: Debug.Assert(currentPair.Value is JsonArray); currentNodes.Pop(); AddToParent(currentPair, ref currentNodes, ref toReturn, options.DuplicatePropertyNameHandling); break; case JsonTokenType.PropertyName: currentNodes.Push(new KeyValuePair <string, JsonNode>(reader.GetString(), null)); break; case JsonTokenType.Number: AddNewPair(new JsonNumber(JsonHelpers.Utf8GetString(reader.ValueSpan))); break; case JsonTokenType.String: AddNewPair(new JsonString(reader.GetString())); break; case JsonTokenType.True: AddNewPair(new JsonBoolean(true)); break; case JsonTokenType.False: AddNewPair(new JsonBoolean(false)); break; case JsonTokenType.Null: AddNewPair(new JsonNull()); break; } } Debug.Assert(toReturn != null); return(toReturn); }
/// <summary> /// Returns true if successful, false is the reader ran out of buffer. /// Sets state.Current.ReturnValue to the reference target for $ref cases; /// Sets state.Current.ReturnValue to a new instance for $id cases. /// </summary> internal static bool ResolveMetadataForJsonObject <T>( ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonConverter converter = state.Current.JsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase; if (state.Current.ObjectState < StackFrameObjectState.ReadAheadNameOrEndObject) { // Read the first metadata property name. if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadNameOrEndObject)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadNameOrEndObject) { if (reader.TokenType != JsonTokenType.PropertyName) { // Since this was an empty object, we are done reading metadata. state.Current.ObjectState = StackFrameObjectState.PropertyValue; // Skip the read of the first property name, since we already read it above. state.Current.PropertyState = StackFramePropertyState.ReadName; return(true); } ReadOnlySpan <byte> propertyName = reader.GetSpan(); MetadataPropertyName metadata = GetMetadataPropertyName(propertyName); if (metadata == MetadataPropertyName.Id) { state.Current.JsonPropertyName = s_idPropertyName; if (!converter.CanHaveIdMetadata) { ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.ReadAheadIdValue; } else if (metadata == MetadataPropertyName.Ref) { state.Current.JsonPropertyName = s_refPropertyName; if (converter.IsValueType) { ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.ReadAheadRefValue; } else if (metadata == MetadataPropertyName.Values) { ThrowHelper.ThrowJsonException_MetadataInvalidPropertyWithLeadingDollarSign(propertyName, ref state, reader); } else { Debug.Assert(metadata == MetadataPropertyName.NoMetadata); // We are done reading metadata, the object didn't contain any. state.Current.ObjectState = StackFrameObjectState.PropertyValue; // Skip the read of the first property name, since we already read it above. state.Current.PropertyState = StackFramePropertyState.ReadName; return(true); } } if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefValue) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefValue)) { return(false); } } else if (state.Current.ObjectState == StackFrameObjectState.ReadAheadIdValue) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadIdValue)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadRefValue) { if (reader.TokenType != JsonTokenType.String) { ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); } string referenceId = reader.GetString() !; object value = state.ReferenceResolver.ResolveReference(referenceId); ValidateValueIsCorrectType <T>(value, referenceId); state.Current.ReturnValue = value; state.Current.ObjectState = StackFrameObjectState.ReadAheadRefEndObject; } else if (state.Current.ObjectState == StackFrameObjectState.ReadIdValue) { if (reader.TokenType != JsonTokenType.String) { ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); } converter.CreateInstanceForReferenceResolver(ref reader, ref state, options); string referenceId = reader.GetString() !; state.ReferenceResolver.AddReference(referenceId, state.Current.ReturnValue !); // We are done reading metadata plus we instantiated the object. state.Current.ObjectState = StackFrameObjectState.CreatedObject; } // Clear the metadata property name that was set in case of failure on ResolveReference/AddReference. state.Current.JsonPropertyName = null; if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefEndObject) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefEndObject)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject) { if (reader.TokenType != JsonTokenType.EndObject) { // We just read a property. The only valid next tokens are EndObject and PropertyName. Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state); } } return(true); }
/// <summary> /// Returns true if successful, false is the reader ran out of buffer. /// Sets state.Current.ReturnValue to the reference target for $ref cases; /// Sets state.Current.ReturnValue to a new instance for $id cases. /// </summary> internal static bool ResolveMetadataForJsonArray <T>( ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonConverter converter = state.Current.JsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase; if (state.Current.ObjectState < StackFrameObjectState.ReadAheadNameOrEndObject) { // Read the first metadata property name. if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadNameOrEndObject)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadNameOrEndObject) { if (reader.TokenType != JsonTokenType.PropertyName) { // The reader should have detected other invalid cases. Debug.Assert(reader.TokenType == JsonTokenType.EndObject); // An enumerable needs metadata since it starts with StartObject. ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(ref state, converter.TypeToConvert); } ReadOnlySpan <byte> propertyName = reader.GetSpan(); MetadataPropertyName metadata = GetMetadataPropertyName(propertyName); if (metadata == MetadataPropertyName.Id) { state.Current.JsonPropertyName = s_idPropertyName; if (!converter.CanHaveIdMetadata) { ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.ReadAheadIdValue; } else if (metadata == MetadataPropertyName.Ref) { state.Current.JsonPropertyName = s_refPropertyName; if (converter.IsValueType) { ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.ReadAheadRefValue; } else if (metadata == MetadataPropertyName.Values) { ThrowHelper.ThrowJsonException_MetadataMissingIdBeforeValues(ref state, propertyName); } else { Debug.Assert(metadata == MetadataPropertyName.NoMetadata); // Having a StartObject without metadata properties is not allowed. ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, converter.TypeToConvert, reader); } } if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefValue) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefValue)) { return(false); } } else if (state.Current.ObjectState == StackFrameObjectState.ReadAheadIdValue) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadIdValue)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadRefValue) { if (reader.TokenType != JsonTokenType.String) { ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); } string referenceId = reader.GetString() !; object value = state.ReferenceResolver.ResolveReference(referenceId); ValidateValueIsCorrectType <T>(value, referenceId); state.Current.ReturnValue = value; state.Current.ObjectState = StackFrameObjectState.ReadAheadRefEndObject; } else if (state.Current.ObjectState == StackFrameObjectState.ReadIdValue) { if (reader.TokenType != JsonTokenType.String) { ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType); } converter.CreateInstanceForReferenceResolver(ref reader, ref state, options); string referenceId = reader.GetString() !; state.ReferenceResolver.AddReference(referenceId, state.Current.ReturnValue !); // Need to Read $values property name. state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesName; } // Clear the metadata property name that was set in case of failure on ResolverReference/AddReference. state.Current.JsonPropertyName = null; if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefEndObject) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefEndObject)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject) { if (reader.TokenType != JsonTokenType.EndObject) { // We just read a property. The only valid next tokens are EndObject and PropertyName. Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state); } return(true); } if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesName) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesName)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadValuesName) { if (reader.TokenType != JsonTokenType.PropertyName) { ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(ref state, converter.TypeToConvert); } ReadOnlySpan <byte> propertyName = reader.GetSpan(); if (GetMetadataPropertyName(propertyName) != MetadataPropertyName.Values) { ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, converter.TypeToConvert, reader); } // Remember the property in case we get an exception in one of the array elements. state.Current.JsonPropertyName = s_valuesPropertyName; state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesStartArray; } if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesStartArray) { if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesStartArray)) { return(false); } } if (state.Current.ObjectState == StackFrameObjectState.ReadValuesStartArray) { // Temporary workaround for the state machine accidentally // erasing the JsonPropertyName property in certain async // re-entrancy patterns. state.Current.JsonPropertyName = s_valuesPropertyName; if (reader.TokenType != JsonTokenType.StartArray) { ThrowHelper.ThrowJsonException_MetadataValuesInvalidToken(reader.TokenType); } state.Current.ValidateEndTokenOnArray = true; state.Current.ObjectState = StackFrameObjectState.CreatedObject; } return(true); }