Пример #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));
            }
        }
Пример #2
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());
        }
Пример #3
0
    public override ElmTestRsReportJsonEntryFailureReasonData Read(
        ref System.Text.Json.Utf8JsonReader reader,
        System.Type typeToConvert,
        System.Text.Json.JsonSerializerOptions options)
    {
        try
        {
            var equalityReader = reader;

            var asEquality = new ElmTestRsReportJsonEntryFailureReasonData(
                Equality: System.Text.Json.JsonSerializer.Deserialize <ElmTestRsReportJsonEntryFailureReasonDataEquality>(ref equalityReader));

            reader = equalityReader;

            return(asEquality);
        }
        catch { }

        return(new ElmTestRsReportJsonEntryFailureReasonData(
                   String: System.Text.Json.JsonSerializer.Deserialize <string>(ref reader)));
    }
Пример #4
0
        private static string GetResourceString(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte, string characters)
        {
            string character = IsPrintable(nextByte) ? ((char)nextByte).ToString() : $"0x{nextByte:X2}";

            string message = "";

            switch (resource)
            {
            case ExceptionResource.ArrayDepthTooLarge:
                message = SR.Format(SR.ArrayDepthTooLarge, json.CurrentState.Options.MaxDepth);
                break;

            case ExceptionResource.MismatchedObjectArray:
                message = SR.Format(SR.MismatchedObjectArray, character);
                break;

            case ExceptionResource.EndOfStringNotFound:
                message = SR.EndOfStringNotFound;
                break;

            case ExceptionResource.RequiredDigitNotFoundAfterSign:
                message = SR.Format(SR.RequiredDigitNotFoundAfterSign, character);
                break;

            case ExceptionResource.RequiredDigitNotFoundAfterDecimal:
                message = SR.Format(SR.RequiredDigitNotFoundAfterDecimal, character);
                break;

            case ExceptionResource.RequiredDigitNotFoundEndOfData:
                message = SR.RequiredDigitNotFoundEndOfData;
                break;

            case ExceptionResource.ExpectedEndAfterSingleJson:
                message = SR.Format(SR.ExpectedEndAfterSingleJson, character);
                break;

            case ExceptionResource.ExpectedEndOfDigitNotFound:
                message = SR.Format(SR.ExpectedEndOfDigitNotFound, character);
                break;

            case ExceptionResource.ExpectedNextDigitEValueNotFound:
                message = SR.Format(SR.ExpectedNextDigitEValueNotFound, character);
                break;

            case ExceptionResource.ExpectedSeparatorAfterPropertyNameNotFound:
                message = SR.Format(SR.ExpectedSeparatorAfterPropertyNameNotFound, character);
                break;

            case ExceptionResource.ExpectedStartOfPropertyNotFound:
                message = SR.Format(SR.ExpectedStartOfPropertyNotFound, character);
                break;

            case ExceptionResource.ExpectedStartOfPropertyOrValueNotFound:
                message = SR.ExpectedStartOfPropertyOrValueNotFound;
                break;

            case ExceptionResource.ExpectedStartOfValueNotFound:
                message = SR.Format(SR.ExpectedStartOfValueNotFound, character);
                break;

            case ExceptionResource.ExpectedValueAfterPropertyNameNotFound:
                message = SR.ExpectedValueAfterPropertyNameNotFound;
                break;

            case ExceptionResource.FoundInvalidCharacter:
                message = SR.Format(SR.FoundInvalidCharacter, character);
                break;

            case ExceptionResource.InvalidEndOfJsonNonPrimitive:
                message = SR.Format(SR.InvalidEndOfJsonNonPrimitive, json.TokenType);
                break;

            case ExceptionResource.ObjectDepthTooLarge:
                message = SR.Format(SR.ObjectDepthTooLarge, json.CurrentState.Options.MaxDepth);
                break;

            case ExceptionResource.ExpectedFalse:
                message = SR.Format(SR.ExpectedFalse, characters);
                break;

            case ExceptionResource.ExpectedNull:
                message = SR.Format(SR.ExpectedNull, characters);
                break;

            case ExceptionResource.ExpectedTrue:
                message = SR.Format(SR.ExpectedTrue, characters);
                break;

            case ExceptionResource.InvalidCharacterWithinString:
                message = SR.Format(SR.InvalidCharacterWithinString, character);
                break;

            case ExceptionResource.InvalidCharacterAfterEscapeWithinString:
                message = SR.Format(SR.InvalidCharacterAfterEscapeWithinString, character);
                break;

            case ExceptionResource.InvalidHexCharacterWithinString:
                message = SR.Format(SR.InvalidHexCharacterWithinString, character);
                break;

            case ExceptionResource.EndOfCommentNotFound:
                message = SR.EndOfCommentNotFound;
                break;

            case ExceptionResource.ZeroDepthAtEnd:
                message = SR.Format(SR.ZeroDepthAtEnd);
                break;

            case ExceptionResource.ExpectedJsonTokens:
                message = SR.ExpectedJsonTokens;
                break;

            default:
                Debug.Fail($"The ExceptionResource enum value: {resource} is not part of the switch. Add the appropriate case and exception message.");
                break;
            }

            return(message);
        }
Пример #5
0
 public static void ThrowJsonReaderException(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte = default, ReadOnlySpan <byte> bytes = default)
 {
     throw GetJsonReaderException(ref json, resource, nextByte, bytes);
 }
Пример #6
0
        private void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, ref ReadStack state, ref Utf8JsonReader reader)
        {
            switch (tokenType)
            {
            case JsonTokenType.StartArray:
                if (reader.TokenType != JsonTokenType.EndArray)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }
                else if (depth != reader.CurrentDepth)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                // Should not be possible to have not read anything.
                Debug.Assert(bytesConsumed < reader.BytesConsumed);
                break;

            case JsonTokenType.StartObject:
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }
                else if (depth != reader.CurrentDepth)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                // Should not be possible to have not read anything.
                Debug.Assert(bytesConsumed < reader.BytesConsumed);
                break;

            default:
                // Reading a single property value.
                if (reader.BytesConsumed != bytesConsumed)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                // Should not be possible to change token type.
                Debug.Assert(reader.TokenType == tokenType);

                break;
            }
        }
Пример #7
0
 protected abstract void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
Пример #8
0
        // If this method is changed, also change JsonPropertyInfoNullable.ReadEnumerable and JsonSerializer.ApplyObjectToEnumerable
        public override void ReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
        {
            Debug.Assert(ShouldDeserialize);

            if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair))
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
                return;
            }

            // We need an initialized array in order to store the values.
            if (state.Current.IsProcessingEnumerable && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null)
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
                return;
            }

            if (ValueConverter == null || !ValueConverter.TryRead(RuntimePropertyType, ref reader, out TRuntimeProperty value))
            {
                if (state.Current.IsProcessingKeyValuePair && state.Current.KeyName == "Key")
                {
                    // Handle the special case where the input KeyValuePair is of form {"Key": "MyKey", "Value": 1}
                    // (as opposed to form {"MyKey": 1}) and the value type is not string.
                    // If we have one, the ValueConverter failed to read the current token because it should be of type string
                    // (we only support string keys) but we initially tried to read it as type TRuntimeProperty.
                    // We have TRuntimeProperty not string because for deserialization, we parse the KeyValuePair as a
                    // dictionary before creating a KeyValuePair instance in a converter-like manner with the parsed values.
                    // Because it's dictionary-like parsing, we set the element type of the dictionary earlier on to the KeyValuePair's value
                    // type, which led us here.
                    // If there's no ValueConverter, the runtime type of the KeyValuePair's value is probably an object, dictionary or enumerable.
                    JsonValueConverter <string> stringConverter = DefaultConverters <string> .s_converter;
                    if (!stringConverter.TryRead(typeof(string), ref reader, out string strValue))
                    {
                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(typeof(string), reader, state.JsonPath);
                    }

                    object objValue = strValue;

                    JsonSerializer.ApplyValueToEnumerable(ref objValue, ref state, ref reader);
                    return;
                }

                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
                return;
            }

            if (state.Current.IsProcessingKeyValuePair)
            {
                // The value is being applied to a Dictionary<string, object>, so we need to cast to object here.
                object objValue = value;
                JsonSerializer.ApplyValueToEnumerable(ref objValue, ref state, ref reader);
                return;
            }

            JsonSerializer.ApplyValueToEnumerable(ref value, ref state, ref reader);
        }
 /// <summary>
 /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>.
 /// </summary>
 /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns>
 /// <param name="reader">The reader to read.</param>
 /// <param name="options">Options to control the serializer behavior during reading.</param>
 /// <exception cref="JsonException">
 /// Thrown when the JSON is invalid,
 /// <typeparamref name="TValue"/> is not compatible with the JSON,
 /// or a value could not be read from the reader.
 /// </exception>
 /// <exception cref="ArgumentException">
 ///   <paramref name="reader"/> is using unsupported options.
 /// </exception>
 /// <remarks>
 ///   <para>
 ///     If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/>
 ///     is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the
 ///     reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine
 ///     the start of the value.
 ///   </para>
 ///
 ///   <para>
 ///     Upon completion of this method <paramref name="reader"/> will be positioned at the
 ///     final token in the JSON value.  If an exception is thrown the reader is reset to
 ///     the state it was in when the method was called.
 ///   </para>
 ///
 ///   <para>
 ///     This method makes a copy of the data the reader acted on, so there is no caller
 ///     requirement to maintain data integrity beyond the return of this method.
 ///   </para>
 ///
 ///   <para>
 ///     The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict.
 ///     Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading.
 ///   </para>
 /// </remarks>
 public static TValue Deserialize <TValue>(ref Utf8JsonReader reader, JsonSerializerOptions options = null)
 {
     return((TValue)ReadValueCore(ref reader, typeof(TValue), options));
 }
        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);
                }

                JsonReaderOptions originalReaderOptions = state.Options;

                var newReader = new Utf8JsonReader(rentedSpan, originalReaderOptions);

                ReadCore(options, ref newReader, ref readStack);

                // The reader should have thrown if we have remaining bytes.
                Debug.Assert(newReader.BytesConsumed == length);
            }
            catch (JsonException)
            {
                reader = restore;
                throw;
            }
            finally
            {
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);
            }
        }
Пример #11
0
        private static void HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            if (state.Current.SkipProperty)
            {
                return;
            }

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
            }
            else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options);
            }

            jsonPropertyInfo.Read(tokenType, ref state, ref reader);
        }
Пример #12
0
        /// <summary>
        /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>.
        /// </summary>
        /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns>
        /// <param name="reader">The reader to read.</param>
        /// <param name="options">Options to control the serializer behavior during reading.</param>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// <typeparamref name="TValue"/> is not compatible with the JSON,
        /// or a value could not be read from the reader.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="reader"/> is using unsupported options.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/>
        /// for <typeparamref name="TValue"/> or its serializable members.
        /// </exception>
        /// <remarks>
        ///   <para>
        ///     If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/>
        ///     is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the
        ///     reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine
        ///     the start of the value.
        ///   </para>
        ///
        ///   <para>
        ///     Upon completion of this method <paramref name="reader"/> will be positioned at the
        ///     final token in the JSON value.  If an exception is thrown the reader is reset to
        ///     the state it was in when the method was called.
        ///   </para>
        ///
        ///   <para>
        ///     This method makes a copy of the data the reader acted on, so there is no caller
        ///     requirement to maintain data integrity beyond the return of this method.
        ///   </para>
        ///
        ///   <para>
        ///     The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict.
        ///     Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading.
        ///   </para>
        /// </remarks>
        public static TValue?Deserialize <[DynamicallyAccessedMembers(MembersAccessedOnRead)] TValue>(ref Utf8JsonReader reader, JsonSerializerOptions?options = null)
        {
            if (options == null)
            {
                options = JsonSerializerOptions.s_defaultOptions;
            }

            ReadStack state = default;

            state.Initialize(typeof(TValue), options, supportContinuation: false);

            return(ReadValueCore <TValue>(options, ref reader, ref state));
        }