Exemple #1
0
        public static void RunSample()
        {
            Console.WriteLine("**Utf8JsonReader Sample***");
            var jsonBytes = File.ReadAllBytes("sample.json");
            var jsonSpan  = jsonBytes.AsSpan();
            var json      = new System.Text.Json.Utf8JsonReader(jsonSpan);

            while (json.Read())
            {
                Console.WriteLine(GetTokenDesc(json));
            }
        }
        private static void ReadValueCore(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack)
        {
            JsonReaderState state = reader.CurrentState;

            CheckSupportedOptions(state.Options, nameof(reader));

            // Value copy to overwrite the ref on an exception and undo the destructive reads.
            Utf8JsonReader restore = reader;

            ReadOnlySpan <byte>     valueSpan     = default;
            ReadOnlySequence <byte> valueSequence = default;

            try
            {
                switch (reader.TokenType)
                {
                // A new reader was created and has never been read,
                // so we need to move to the first token.
                // (or a reader has terminated and we're about to throw)
                case JsonTokenType.None:
                // Using a reader loop the caller has identified a property they wish to
                // hydrate into a JsonDocument. Move to the value first.
                case JsonTokenType.PropertyName:
                {
                    if (!reader.Read())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.ExpectedOneCompleteToken);
                    }
                    break;
                }
                }

                switch (reader.TokenType)
                {
                // Any of the "value start" states are acceptable.
                case JsonTokenType.StartObject:
                case JsonTokenType.StartArray:
                {
                    long startingOffset = reader.TokenStartIndex;

                    if (!reader.TrySkip())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.NotEnoughData);
                    }

                    long totalLength = reader.BytesConsumed - startingOffset;
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        valueSpan = reader.OriginalSpan.Slice(
                            checked ((int)startingOffset),
                            checked ((int)totalLength));
                    }
                    else
                    {
                        valueSequence = sequence.Slice(startingOffset, totalLength);
                    }

                    Debug.Assert(
                        reader.TokenType == JsonTokenType.EndObject ||
                        reader.TokenType == JsonTokenType.EndArray);

                    break;
                }

                // Single-token values
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                {
                    if (reader.HasValueSequence)
                    {
                        valueSequence = reader.ValueSequence;
                    }
                    else
                    {
                        valueSpan = reader.ValueSpan;
                    }

                    break;
                }

                // String's ValueSequence/ValueSpan omits the quotes, we need them back.
                case JsonTokenType.String:
                {
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        // Since the quoted string fit in a ReadOnlySpan originally
                        // the contents length plus the two quotes can't overflow.
                        int payloadLength = reader.ValueSpan.Length + 2;
                        Debug.Assert(payloadLength > 1);

                        ReadOnlySpan <byte> readerSpan = reader.OriginalSpan;

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
                            $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
                            $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}");

                        valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
                    }
                    else
                    {
                        long payloadLength = 2;

                        if (reader.HasValueSequence)
                        {
                            payloadLength += reader.ValueSequence.Length;
                        }
                        else
                        {
                            payloadLength += reader.ValueSpan.Length;
                        }

                        valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength);
                        Debug.Assert(
                            valueSequence.First.Span[0] == (byte)'"',
                            $"Calculated sequence starts with {valueSequence.First.Span[0]}");

                        Debug.Assert(
                            valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
                            $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
                    }

                    break;
                }

                default:
                {
                    byte displayByte;

                    if (reader.HasValueSequence)
                    {
                        displayByte = reader.ValueSequence.First.Span[0];
                    }
                    else
                    {
                        displayByte = reader.ValueSpan[0];
                    }

                    ThrowHelper.ThrowJsonReaderException(
                        ref reader,
                        ExceptionResource.ExpectedStartOfValueNotFound,
                        displayByte);

                    break;
                }
                }
            }
            catch (JsonReaderException ex)
            {
                reader = restore;
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(readStack, ex);
            }

            int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length;

            byte[] rented = ArrayPool <byte> .Shared.Rent(length);

            Span <byte> rentedSpan = rented.AsSpan(0, length);

            try
            {
                if (valueSpan.IsEmpty)
                {
                    valueSequence.CopyTo(rentedSpan);
                }
                else
                {
                    valueSpan.CopyTo(rentedSpan);
                }

                var newReader = new Utf8JsonReader(rentedSpan, isFinalBlock: true, state: default);

                ReadCore(options, ref newReader, ref readStack);

                if (newReader.BytesConsumed != length)
                {
                    state = newReader.CurrentState;
                    ThrowHelper.ThrowJsonException_DeserializeDataRemaining(length, length - state.BytesConsumed);
                }
            }
            catch (JsonException)
            {
                reader = restore;
                throw;
            }
            finally
            {
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);
            }
        }
        private static bool TryParseValue(ref Utf8JsonReader reader, out JsonDocument document, bool shouldThrow)
        {
            JsonReaderState state = reader.CurrentState;

            CheckSupportedOptions(state.Options, nameof(reader));

            // Value copy to overwrite the ref on an exception and undo the destructive reads.
            Utf8JsonReader restore = reader;

            ReadOnlySpan <byte>     valueSpan     = default;
            ReadOnlySequence <byte> valueSequence = default;

            try
            {
                switch (reader.TokenType)
                {
                // A new reader was created and has never been read,
                // so we need to move to the first token.
                // (or a reader has terminated and we're about to throw)
                case JsonTokenType.None:
                // Using a reader loop the caller has identified a property they wish to
                // hydrate into a JsonDocument. Move to the value first.
                case JsonTokenType.PropertyName:
                {
                    if (!reader.Read())
                    {
                        if (shouldThrow)
                        {
                            ThrowHelper.ThrowJsonReaderException(
                                ref reader,
                                ExceptionResource.ExpectedJsonTokens);
                        }

                        reader   = restore;
                        document = null;
                        return(false);
                    }
                    break;
                }
                }

                switch (reader.TokenType)
                {
                // Any of the "value start" states are acceptable.
                case JsonTokenType.StartObject:
                case JsonTokenType.StartArray:
                {
                    long startingOffset = reader.TokenStartIndex;

                    // Placeholder until reader.Skip() is written (#33295)
                    {
                        int depth = reader.CurrentDepth;

                        // CurrentDepth rises late and falls fast,
                        // a payload of "[ 1, 2, 3, 4 ]" will report post-Read()
                        // CurrentDepth values of { 0, 1, 1, 1, 1, 0 },
                        // Since we're logically at 0 ([), Read() once and keep
                        // reading until we've come back down to 0 (]).
                        do
                        {
                            if (!reader.Read())
                            {
                                if (shouldThrow)
                                {
                                    ThrowHelper.ThrowJsonReaderException(
                                        ref reader,
                                        ExceptionResource.ExpectedJsonTokens);
                                }

                                reader   = restore;
                                document = null;
                                return(false);
                            }
                        } while (reader.CurrentDepth > depth);
                    }

                    long totalLength = reader.BytesConsumed - startingOffset;
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        valueSpan = reader.OriginalSpan.Slice(
                            checked ((int)startingOffset),
                            checked ((int)totalLength));
                    }
                    else
                    {
                        valueSequence = sequence.Slice(startingOffset, totalLength);
                    }

                    Debug.Assert(
                        reader.TokenType == JsonTokenType.EndObject ||
                        reader.TokenType == JsonTokenType.EndArray);

                    break;
                }

                // Single-token values
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                {
                    if (reader.HasValueSequence)
                    {
                        valueSequence = reader.ValueSequence;
                    }
                    else
                    {
                        valueSpan = reader.ValueSpan;
                    }

                    break;
                }

                // String's ValueSequence/ValueSpan omits the quotes, we need them back.
                case JsonTokenType.String:
                {
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        // Since the quoted string fit in a ReadOnlySpan originally
                        // the contents length plus the two quotes can't overflow.
                        int payloadLength = reader.ValueSpan.Length + 2;
                        Debug.Assert(payloadLength > 1);

                        ReadOnlySpan <byte> readerSpan = reader.OriginalSpan;

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
                            $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
                            $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}");

                        valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
                    }
                    else
                    {
                        long payloadLength = 2;

                        if (reader.HasValueSequence)
                        {
                            payloadLength += reader.ValueSequence.Length;
                        }
                        else
                        {
                            payloadLength += reader.ValueSpan.Length;
                        }

                        valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength);
                        Debug.Assert(
                            valueSequence.First.Span[0] == (byte)'"',
                            $"Calculated sequence starts with {valueSequence.First.Span[0]}");

                        Debug.Assert(
                            valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
                            $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
                    }

                    break;
                }

                default:
                {
                    if (shouldThrow)
                    {
                        byte displayByte;

                        if (reader.HasValueSequence)
                        {
                            displayByte = reader.ValueSequence.First.Span[0];
                        }
                        else
                        {
                            displayByte = reader.ValueSpan[0];
                        }

                        ThrowHelper.ThrowJsonReaderException(
                            ref reader,
                            ExceptionResource.ExpectedStartOfValueNotFound,
                            displayByte);
                    }

                    reader   = restore;
                    document = null;
                    return(false);
                }
                }
            }
            catch
            {
                reader = restore;
                throw;
            }

            int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length;

            byte[] rented = ArrayPool <byte> .Shared.Rent(length);

            Span <byte> rentedSpan = rented.AsSpan(0, length);

            try
            {
                if (valueSpan.IsEmpty)
                {
                    valueSequence.CopyTo(rentedSpan);
                }
                else
                {
                    valueSpan.CopyTo(rentedSpan);
                }

                document = Parse(rented.AsMemory(0, length), state.Options, rented);
                return(true);
            }
            catch
            {
                // This really shouldn't happen since the document was already checked
                // for consistency by Skip.  But if data mutations happened just after
                // the calls to Read then the copy may not be valid.
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);

                throw;
            }
        }
Exemple #4
0
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack readStack)
        {
            try
            {
                JsonReaderState initialState = default;

                while (true)
                {
                    if (readStack.ReadAhead)
                    {
                        // When we're reading ahead we always have to save the state
                        // as we don't know if the next token is an opening object or
                        // array brace.
                        initialState = reader.CurrentState;
                    }

                    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 || readStack.Current.IsProcessingImmutableDictionary)
                        {
                            HandleStartDictionary(options, ref reader, ref readStack);
                        }
                        else
                        {
                            HandleStartObject(options, ref reader, ref readStack);
                        }
                    }
                    else if (tokenType == JsonTokenType.EndObject)
                    {
                        if (readStack.Current.Drain)
                        {
                            readStack.Pop();
                        }
                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingImmutableDictionary)
                        {
                            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;
        }
        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);
        }
        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);
        }
        /// <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(JsonNull.Instance);
                    break;
                }
            }

            Debug.Assert(toReturn != null);
            return(toReturn);
        }
        private static Utf8JsonReader GetReaderScopedToNextValue(ref Utf8JsonReader reader, scoped ref ReadStack state)
        {
            // Advances the provided reader, validating that it is pointing to a complete JSON value.
            // If successful, returns a new Utf8JsonReader that is scoped to the next value, reusing existing buffers.

            ReadOnlySpan <byte>     valueSpan     = default;
            ReadOnlySequence <byte> valueSequence = default;

            try
            {
                switch (reader.TokenType)
                {
                // A new reader was created and has never been read,
                // so we need to move to the first token.
                // (or a reader has terminated and we're about to throw)
                case JsonTokenType.None:
                // Using a reader loop the caller has identified a property they wish to
                // hydrate into a JsonDocument. Move to the value first.
                case JsonTokenType.PropertyName:
                {
                    if (!reader.Read())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.ExpectedOneCompleteToken);
                    }
                    break;
                }
                }

                switch (reader.TokenType)
                {
                // Any of the "value start" states are acceptable.
                case JsonTokenType.StartObject:
                case JsonTokenType.StartArray:
                    long startingOffset = reader.TokenStartIndex;

                    if (!reader.TrySkip())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.NotEnoughData);
                    }

                    long totalLength = reader.BytesConsumed - startingOffset;
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        valueSpan = reader.OriginalSpan.Slice(
                            checked ((int)startingOffset),
                            checked ((int)totalLength));
                    }
                    else
                    {
                        valueSequence = sequence.Slice(startingOffset, totalLength);
                    }

                    Debug.Assert(reader.TokenType is JsonTokenType.EndObject or JsonTokenType.EndArray);
                    break;

                // Single-token values
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                    if (reader.HasValueSequence)
                    {
                        valueSequence = reader.ValueSequence;
                    }
                    else
                    {
                        valueSpan = reader.ValueSpan;
                    }

                    break;

                // String's ValueSequence/ValueSpan omits the quotes, we need them back.
                case JsonTokenType.String:
                    ReadOnlySequence <byte> originalSequence = reader.OriginalSequence;

                    if (originalSequence.IsEmpty)
                    {
                        // Since the quoted string fit in a ReadOnlySpan originally
                        // the contents length plus the two quotes can't overflow.
                        int payloadLength = reader.ValueSpan.Length + 2;
                        Debug.Assert(payloadLength > 1);

                        ReadOnlySpan <byte> readerSpan = reader.OriginalSpan;

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
                            $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
                            $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}");

                        valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
                    }
                    else
                    {
                        long payloadLength = reader.HasValueSequence
                                ? reader.ValueSequence.Length + 2
                                : reader.ValueSpan.Length + 2;

                        valueSequence = originalSequence.Slice(reader.TokenStartIndex, payloadLength);
                        Debug.Assert(
                            valueSequence.First.Span[0] == (byte)'"',
                            $"Calculated sequence starts with {valueSequence.First.Span[0]}");

                        Debug.Assert(
                            valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
                            $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
                    }

                    break;

                default:
                    byte displayByte = reader.HasValueSequence
                            ? reader.ValueSequence.First.Span[0]
                            : reader.ValueSpan[0];

                    ThrowHelper.ThrowJsonReaderException(
                        ref reader,
                        ExceptionResource.ExpectedStartOfValueNotFound,
                        displayByte);

                    break;
                }
            }
            catch (JsonReaderException ex)
            {
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(ref state, ex);
            }

            Debug.Assert(!valueSpan.IsEmpty ^ !valueSequence.IsEmpty);

            return(valueSpan.IsEmpty
                ? new Utf8JsonReader(valueSequence, reader.CurrentState.Options)
                : new Utf8JsonReader(valueSpan, reader.CurrentState.Options));
        }
Exemple #9
0
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            try
            {
                JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo !.PolicyProperty !;
                JsonConverter    converter        = jsonPropertyInfo.ConverterBase;

                if (!state.IsContinuation)
                {
                    if (!JsonConverter.SingleValueReadWithReadAhead(converter.ClassType, ref reader, ref state))
                    {
                        // Read more data until we have the full element.
                        state.BytesConsumed += reader.BytesConsumed;
                        return;
                    }
                }
                else
                {
                    // For a continuation, read ahead here to avoid having to build and then tear
                    // down the call stack if there is more than one buffer fetch necessary.
                    if (!JsonConverter.SingleValueReadWithReadAhead(ClassType.Value, ref reader, ref state))
                    {
                        state.BytesConsumed += reader.BytesConsumed;
                        return;
                    }
                }

                bool success = converter.TryReadAsObject(ref reader, jsonPropertyInfo.RuntimePropertyType !, options, ref state, out object?value);
                if (success)
                {
                    state.Current.ReturnValue = value;

                    // Read any trailing whitespace.
                    // If additional whitespace exists after this read, the subsequent call to reader.Read() will throw.
                    reader.Read();
                }

                state.BytesConsumed += reader.BytesConsumed;
            }
            catch (JsonReaderException ex)
            {
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(state, ex);
            }
            catch (FormatException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(state, reader, ex);
            }
            catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(state, reader, ex);
            }
            catch (JsonException ex)
            {
                ThrowHelper.AddExceptionInformation(state, reader, ex);
                throw;
            }
        }
Exemple #10
0
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack readStack)
        {
            try
            {
                JsonReaderState initialState         = default;
                long            initialBytesConsumed = default;

                while (true)
                {
                    if (readStack.ReadAhead)
                    {
                        // When we're reading ahead we always have to save the state
                        // as we don't know if the next token is an opening object or
                        // array brace.
                        initialState         = reader.CurrentState;
                        initialBytesConsumed = reader.BytesConsumed;
                    }

                    if (!reader.Read())
                    {
                        // Need more data
                        break;
                    }

                    JsonTokenType tokenType = reader.TokenType;

                    if (JsonHelpers.IsInRangeInclusive(tokenType, JsonTokenType.String, JsonTokenType.False))
                    {
                        Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False);

                        HandleValue(tokenType, options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.PropertyName)
                    {
                        HandlePropertyName(options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.StartObject)
                    {
                        if (readStack.Current.SkipProperty)
                        {
                            readStack.Push();
                            readStack.Current.Drain = true;
                        }
                        else if (readStack.Current.IsProcessingValue())
                        {
                            if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed))
                            {
                                // Need more data
                                break;
                            }
                        }
                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
                        {
                            HandleStartDictionary(options, ref reader, ref readStack);
                        }
                        else
                        {
                            HandleStartObject(options, ref readStack);
                        }
                    }
                    else if (tokenType == JsonTokenType.EndObject)
                    {
                        if (readStack.Current.Drain)
                        {
                            readStack.Pop();

                            // Clear the current property in case it is a dictionary, since dictionaries must have EndProperty() called when completed.
                            // A non-dictionary property can also have EndProperty() called when completed, although it is redundant.
                            readStack.Current.EndProperty();
                        }
                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
                        {
                            HandleEndDictionary(options, ref reader, ref readStack);
                        }
                        else
                        {
                            HandleEndObject(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, initialBytesConsumed))
                        {
                            // Need more data
                            break;
                        }
                    }
                    else if (tokenType == JsonTokenType.EndArray)
                    {
                        HandleEndArray(options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.Null)
                    {
                        HandleNull(ref reader, ref readStack);
                    }
                }
            }
            catch (JsonReaderException ex)
            {
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(readStack, ex);
            }
            catch (FormatException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(readStack, reader, ex);
            }
            catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(readStack, reader, ex);
            }
            catch (JsonException ex)
            {
                ThrowHelper.AddExceptionInformation(readStack, reader, ex);
                throw;
            }

            readStack.BytesConsumed += reader.BytesConsumed;
            return;
        }
Exemple #11
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;
            }
        }