Exemple #1
0
        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());
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
Exemple #6
0
        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;
            }
        }
Exemple #7
0
        /// <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);
        }
Exemple #8
0
        /// <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);
        }
Exemple #9
0
        /// <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);
        }