Esempio n. 1
0
        private static TValue?ReadFromSpan <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo jsonTypeInfo, int?actualByteCount = null)
        {
            JsonSerializerOptions options = jsonTypeInfo.Options;

            var readerState = new JsonReaderState(options.GetReaderOptions());
            var reader      = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            TValue?       value;
            JsonConverter jsonConverter = jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase;

            // For performance, the code below is a lifted ReadCore() above.
            if (jsonConverter is JsonConverter <TValue> converter)
            {
                // Call the strongly-typed ReadCore that will not box structs.
                value = converter.ReadCore(ref reader, options, ref state);
            }
            else
            {
                // The non-generic API was called or we have a polymorphic case where TValue is not equal to the T in JsonConverter<T>.
                object?objValue = jsonConverter.ReadCoreAsObject(ref reader, options, ref state);
                Debug.Assert(objValue == null || objValue is TValue);
                value = (TValue?)objValue;
            }

            // The reader should have thrown if we have remaining bytes.
            Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length));
            return(value);
        }
Esempio n. 2
0
            static async IAsyncEnumerable <TValue> CreateAsyncEnumerableDeserializer(
                Stream utf8Json,
                JsonSerializerOptions options,
                [EnumeratorCancellation] CancellationToken cancellationToken)
            {
                var       bufferState = new ReadAsyncBufferState(options.DefaultBufferSize);
                ReadStack readStack   = default;

                readStack.Initialize(typeof(Queue <TValue>), options, supportContinuation: true);
                JsonConverter converter       = readStack.Current.JsonPropertyInfo !.ConverterBase;
                var           jsonReaderState = new JsonReaderState(options.GetReaderOptions());

                try
                {
                    do
                    {
                        bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false);

                        ContinueDeserialize <Queue <TValue> >(ref bufferState, ref jsonReaderState, ref readStack, converter, options);
                        if (readStack.Current.ReturnValue is Queue <TValue> queue)
                        {
                            while (queue.Count > 0)
                            {
                                yield return(queue.Dequeue());
                            }
                        }
                    }while (!bufferState.IsFinalBlock);
                }
                finally
                {
                    bufferState.Dispose();
                }
            }
        internal static TValue?ReadAll <TValue>(
            Stream utf8Json,
            JsonTypeInfo jsonTypeInfo)
        {
            JsonSerializerOptions options = jsonTypeInfo.Options;
            var       bufferState         = new ReadBufferState(options.DefaultBufferSize);
            ReadStack readStack           = default;

            jsonTypeInfo.EnsureConfigured();
            readStack.Initialize(jsonTypeInfo, supportContinuation: true);
            JsonConverter converter       = readStack.Current.JsonPropertyInfo !.ConverterBase;
            var           jsonReaderState = new JsonReaderState(options.GetReaderOptions());

            try
            {
                while (true)
                {
                    bufferState = ReadFromStream(utf8Json, bufferState);
                    TValue value = ContinueDeserialize <TValue>(ref bufferState, ref jsonReaderState, ref readStack, converter, options);

                    if (bufferState.IsFinalBlock)
                    {
                        return(value !);
                    }
                }
            }
            finally
            {
                bufferState.Dispose();
            }
        }
            static async IAsyncEnumerable <TValue> CreateAsyncEnumerableDeserializer(
                Stream utf8Json,
                JsonSerializerOptions options,
                [EnumeratorCancellation] CancellationToken cancellationToken)
            {
                var bufferState = new ReadBufferState(options.DefaultBufferSize);
                // Hardcode the queue converter to avoid accidental use of custom converters
                JsonConverter converter    = QueueOfTConverter <Queue <TValue>, TValue> .Instance;
                JsonTypeInfo  jsonTypeInfo = CreateQueueJsonTypeInfo <TValue>(converter, options);
                ReadStack     readStack    = default;

                readStack.Initialize(jsonTypeInfo, supportContinuation: true);
                var jsonReaderState = new JsonReaderState(options.GetReaderOptions());

                try
                {
                    do
                    {
                        bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false);

                        ContinueDeserialize <Queue <TValue> >(ref bufferState, ref jsonReaderState, ref readStack, converter, options);
                        if (readStack.Current.ReturnValue is Queue <TValue> queue)
                        {
                            while (queue.Count > 0)
                            {
                                yield return(queue.Dequeue());
                            }
                        }
                    }while (!bufferState.IsFinalBlock);
                }
                finally
                {
                    bufferState.Dispose();
                }
            }
        private static TValue?Read <TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo)
        {
            Debug.Assert(jsonTypeInfo.IsConfigured);

            if (reader.CurrentState.Options.CommentHandling == JsonCommentHandling.Allow)
            {
                ThrowHelper.ThrowArgumentException_SerializerDoesNotSupportComments(nameof(reader));
            }

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);
            Utf8JsonReader restore = reader;

            try
            {
                Utf8JsonReader scopedReader = GetReaderScopedToNextValue(ref reader, ref state);
                return(ReadCore <TValue>(ref scopedReader, jsonTypeInfo, ref state));
            }
            catch (JsonException)
            {
                reader = restore;
                throw;
            }
        }
Esempio n. 6
0
        private static TValue?ReadFromSpan <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo jsonTypeInfo, int?actualByteCount = null)
        {
            Debug.Assert(jsonTypeInfo.IsConfigured);

            JsonSerializerOptions options = jsonTypeInfo.Options;

            var readerState = new JsonReaderState(options.GetReaderOptions());
            var reader      = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            TValue?value;

            // For performance, the code below is a lifted ReadCore() above.
            if (jsonTypeInfo is JsonTypeInfo <TValue> typedInfo)
            {
                // Call the strongly-typed ReadCore that will not box structs.
                value = typedInfo.EffectiveConverter.ReadCore(ref reader, options, ref state);
            }
            else
            {
                // The non-generic API was called.
                object?objValue = jsonTypeInfo.Converter.ReadCoreAsObject(ref reader, options, ref state);
                Debug.Assert(objValue is null or TValue);
                value = (TValue?)objValue;
            }

            // The reader should have thrown if we have remaining bytes.
            Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length));
            return(value);
        }
Esempio n. 7
0
        private static TValue ReadCore <TValue>(ref Utf8JsonReader reader, Type returnType, JsonSerializerOptions options)
        {
            ReadStack state = default;

            state.Initialize(returnType, options, supportContinuation: false);
            JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase;

            return(ReadCore <TValue>(jsonConverter, ref reader, options, ref state));
        }
        /// <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="jsonTypeInfo">Metadata about the type to convert.</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 <TValue>(ref Utf8JsonReader reader, JsonTypeInfo <TValue> jsonTypeInfo)
        {
            if (jsonTypeInfo == null)
            {
                throw new ArgumentNullException(nameof(jsonTypeInfo));
            }

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            return(ReadValueCore <TValue>(jsonTypeInfo.Options, ref reader, ref state));
        }
        /// <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));
        }
Esempio n. 10
0
        private static async IAsyncEnumerable <TValue> CreateAsyncEnumerableDeserializer <TValue>(
            Stream utf8Json,
            JsonTypeInfo jsonTypeInfo,
            [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            JsonSerializerOptions          options       = jsonTypeInfo.Options;
            JsonTypeInfo <Queue <TValue> > queueTypeInfo =
                JsonMetadataServices.CreateQueueInfo <Queue <TValue>, TValue>(
                    options: options,
                    collectionInfo: new()
            {
                ObjectCreator  = () => new Queue <TValue>(),
                ElementInfo    = jsonTypeInfo,
                NumberHandling = options.NumberHandling
            });

            var       bufferState = new ReadBufferState(options.DefaultBufferSize);
            ReadStack readStack   = default;

            queueTypeInfo.EnsureConfigured();
            readStack.Initialize(queueTypeInfo, supportContinuation: true);
            var jsonReaderState = new JsonReaderState(options.GetReaderOptions());

            try
            {
                do
                {
                    bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false);

                    ContinueDeserialize <Queue <TValue> >(
                        ref bufferState,
                        ref jsonReaderState,
                        ref readStack,
                        queueTypeInfo.PropertyInfoForTypeInfo.ConverterBase,
                        options);

                    if (readStack.Current.ReturnValue is Queue <TValue> queue)
                    {
                        while (queue.Count > 0)
                        {
                            yield return(queue.Dequeue());
                        }
                    }
                }while (!bufferState.IsFinalBlock);
            }
            finally
            {
                bufferState.Dispose();
            }
        }
        private static TValue?ReadUsingMetadata <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo jsonTypeInfo, int?actualByteCount = null)
        {
            JsonSerializerOptions options = jsonTypeInfo.Options;

            var readerState = new JsonReaderState(options.GetReaderOptions());
            var reader      = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            TValue?value = ReadCore <TValue>(jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, ref reader, options, ref state);

            // The reader should have thrown if we have remaining bytes.
            Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length));
            return(value);
        }
Esempio n. 12
0
        private static TValue?DeserializeUsingMetadata <TValue>(ReadOnlySpan <char> json, JsonTypeInfo?jsonTypeInfo)
        {
            if (jsonTypeInfo == null)
            {
                throw new ArgumentNullException(nameof(jsonTypeInfo));
            }

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            return(Deserialize <TValue>(
                       jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase,
                       json,
                       jsonTypeInfo.Options,
                       ref state));
        }
Esempio n. 13
0
        private static TValue?Deserialize <TValue>(ReadOnlySpan <char> json, Type returnType, JsonSerializerOptions?options)
        {
            if (options == null)
            {
                options = JsonSerializerOptions.s_defaultOptions;
            }

            options.RootBuiltInConvertersAndTypeInfoCreator();

            ReadStack state = default;

            state.Initialize(returnType, options, supportContinuation: false);

            JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase;

            return(Deserialize <TValue>(jsonConverter, json, options, ref state));
        }
Esempio n. 14
0
        /// <summary>
        /// Parse the UTF-8 encoded text representing a single JSON value into a <typeparamref name="TValue"/>.
        /// </summary>
        /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns>
        /// <param name="utf8Json">JSON text to parse.</param>
        /// <param name="jsonTypeInfo">Metadata about the type to convert.</param>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// <typeparamref name="TValue"/> is not compatible with the JSON,
        /// or when there is remaining data in the Stream.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/>
        /// for <typeparamref name="TValue"/> or its serializable members.
        /// </exception>
        public static TValue?Deserialize <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo <TValue> jsonTypeInfo)
        {
            if (jsonTypeInfo == null)
            {
                throw new ArgumentNullException(nameof(jsonTypeInfo));
            }

            JsonSerializerOptions options = jsonTypeInfo.Options;

            var readerState = new JsonReaderState(options.GetReaderOptions());
            var reader      = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            return(ReadCore <TValue>(jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, ref reader, options, ref state));
        }
        /// <summary>
        /// Reads one JSON value (including objects or arrays) from the provided reader into a <paramref name="returnType"/>.
        /// </summary>
        /// <returns>A <paramref name="returnType"/> representation of the JSON value.</returns>
        /// <param name="reader">The reader to read.</param>
        /// <param name="returnType">The type of the object to convert to and return.</param>
        /// <param name="options">Options to control the serializer behavior during reading.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="returnType"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// <paramref name="returnType"/> 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 <paramref name="returnType"/> 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 object?Deserialize(ref Utf8JsonReader reader, [DynamicallyAccessedMembers(MembersAccessedOnRead)] Type returnType, JsonSerializerOptions?options = null)
        {
            if (returnType == null)
            {
                throw new ArgumentNullException(nameof(returnType));
            }

            if (options == null)
            {
                options = JsonSerializerOptions.s_defaultOptions;
            }

            ReadStack state = default;

            state.Initialize(returnType, options, supportContinuation: false);

            return(ReadValueCore <object>(options, ref reader, ref state));
        }
        /// <summary>
        /// Reads one JSON value (including objects or arrays) from the provided reader into a <paramref name="returnType"/>.
        /// </summary>
        /// <returns>A <paramref name="returnType"/> representation of the JSON value.</returns>
        /// <param name="reader">The reader to read.</param>
        /// <param name="returnType">The type of the object to convert to and return.</param>
        /// <param name="context">A metadata provider for serializable types.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="returnType"/> or <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// <paramref name="returnType"/> 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 <paramref name="returnType"/> or its serializable members.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// The <see cref="JsonSerializerContext.GetTypeInfo(Type)"/> method on the provided <paramref name="context"/>
        /// did not return a compatible <see cref="JsonTypeInfo"/> for <paramref name="returnType"/>.
        /// </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 object?Deserialize(ref Utf8JsonReader reader, Type returnType, JsonSerializerContext context)
        {
            if (returnType == null)
            {
                throw new ArgumentNullException(nameof(returnType));
            }

            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ReadStack state = default;

            JsonTypeInfo jsonTypeInfo = JsonHelpers.GetTypeInfo(context, returnType);

            state.Initialize(jsonTypeInfo);

            return(ReadValueCore <object>(jsonTypeInfo.Options, ref reader, ref state));
        }
Esempio n. 17
0
        /// <summary>
        /// Parse the UTF-8 encoded text representing a single JSON value into a <paramref name="returnType"/>.
        /// </summary>
        /// <returns>A <paramref name="returnType"/> representation of the JSON value.</returns>
        /// <param name="utf8Json">JSON text to parse.</param>
        /// <param name="returnType">The type of the object to convert to and return.</param>
        /// <param name="context">A metadata provider for serializable types.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="returnType"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// <paramref name="returnType"/> is not compatible with the JSON,
        /// or when there is remaining data in the Stream.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/>
        /// for <paramref name="returnType"/> or its serializable members.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// The <see cref="JsonSerializerContext.GetTypeInfo(Type)"/> method on the provided <paramref name="context"/>
        /// did not return a compatible <see cref="JsonTypeInfo"/> for <paramref name="returnType"/>.
        /// </exception>
        public static object?Deserialize(ReadOnlySpan <byte> utf8Json, Type returnType, JsonSerializerContext context)
        {
            if (returnType == null)
            {
                throw new ArgumentNullException(nameof(returnType));
            }

            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            JsonTypeInfo          jsonTypeInfo = JsonHelpers.GetTypeInfo(context, returnType);
            JsonSerializerOptions options      = jsonTypeInfo.Options;

            var readerState = new JsonReaderState(options.GetReaderOptions());
            var reader      = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);

            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            return(ReadCore <object?>(jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, ref reader, options, ref state));
        }
Esempio n. 18
0
        private static async ValueTask <TValue> ReadAsync <TValue>(
            Stream utf8Json,
            Type returnType,
            JsonSerializerOptions?options,
            CancellationToken cancellationToken)
        {
            if (options == null)
            {
                options = JsonSerializerOptions.s_defaultOptions;
            }

            ReadStack state = default;

            state.Initialize(returnType, options, supportContinuation: true);

            JsonConverter converter = state.Current.JsonPropertyInfo !.ConverterBase;

            var readerState = new JsonReaderState(options.GetReaderOptions());

            // todo: https://github.com/dotnet/runtime/issues/32355
            int utf8BomLength = JsonConstants.Utf8Bom.Length;

            byte[] buffer = ArrayPool <byte> .Shared.Rent(Math.Max(options.DefaultBufferSize, utf8BomLength));

            int  bytesInBuffer    = 0;
            long totalBytesRead   = 0;
            int  clearMax         = 0;
            bool isFirstIteration = true;

            try
            {
                while (true)
                {
                    // Read from the stream until either our buffer is filled or we hit EOF.
                    // Calling ReadCore is relatively expensive, so we minimize the number of times
                    // we need to call it.
                    bool isFinalBlock = false;
                    while (true)
                    {
                        int bytesRead = await utf8Json.ReadAsync(
#if BUILDING_INBOX_LIBRARY
                            buffer.AsMemory(bytesInBuffer),
#else
                            buffer, bytesInBuffer, buffer.Length - bytesInBuffer,
#endif
                            cancellationToken).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            isFinalBlock = true;
                            break;
                        }

                        totalBytesRead += bytesRead;
                        bytesInBuffer  += bytesRead;

                        if (bytesInBuffer == buffer.Length)
                        {
                            break;
                        }
                    }

                    if (bytesInBuffer > clearMax)
                    {
                        clearMax = bytesInBuffer;
                    }

                    int start = 0;
                    if (isFirstIteration)
                    {
                        isFirstIteration = false;

                        // Handle the UTF-8 BOM if present
                        Debug.Assert(buffer.Length >= JsonConstants.Utf8Bom.Length);
                        if (buffer.AsSpan().StartsWith(JsonConstants.Utf8Bom))
                        {
                            start         += utf8BomLength;
                            bytesInBuffer -= utf8BomLength;
                        }
                    }

                    // Process the data available
                    TValue value = ReadCore <TValue>(
                        ref readerState,
                        isFinalBlock,
                        new ReadOnlySpan <byte>(buffer, start, bytesInBuffer),
                        options,
                        ref state,
                        converter);

                    Debug.Assert(state.BytesConsumed <= bytesInBuffer);
                    int bytesConsumed = checked ((int)state.BytesConsumed);

                    bytesInBuffer -= bytesConsumed;

                    if (isFinalBlock)
                    {
                        // The reader should have thrown if we have remaining bytes.
                        Debug.Assert(bytesInBuffer == 0);

                        return(value);
                    }

                    // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization.
                    if ((uint)bytesInBuffer > ((uint)buffer.Length / 2))
                    {
                        // We have less than half the buffer available, double the buffer size.
                        byte[] dest = ArrayPool <byte> .Shared.Rent((buffer.Length < (int.MaxValue / 2))?buffer.Length * 2 : int.MaxValue);

                        // Copy the unprocessed data to the new buffer while shifting the processed bytes.
                        Buffer.BlockCopy(buffer, bytesConsumed + start, dest, 0, bytesInBuffer);

                        new Span <byte>(buffer, 0, clearMax).Clear();
                        ArrayPool <byte> .Shared.Return(buffer);

                        clearMax = bytesInBuffer;
                        buffer   = dest;
                    }
                    else if (bytesInBuffer != 0)
                    {
                        // Shift the processed bytes to the beginning of buffer to make more room.
                        Buffer.BlockCopy(buffer, bytesConsumed + start, buffer, 0, bytesInBuffer);
                    }
                }
            }
            finally
            {
                // Clear only what we used and return the buffer to the pool
                new Span <byte>(buffer, 0, clearMax).Clear();
                ArrayPool <byte> .Shared.Return(buffer);
            }
        }
        private static TValue?ReadUsingMetadata <TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo)
        {
            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            JsonReaderState readerState = reader.CurrentState;

            if (readerState.Options.CommentHandling == JsonCommentHandling.Allow)
            {
                throw new ArgumentException(SR.JsonSerializerDoesNotSupportComments, 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(state, 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 = readerState.Options;

                var newReader = new Utf8JsonReader(rentedSpan, originalReaderOptions);

                JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase;
                TValue?       value         = ReadCore <TValue>(jsonConverter, ref newReader, jsonTypeInfo.Options, ref state);

                // The reader should have thrown if we have remaining bytes.
                Debug.Assert(newReader.BytesConsumed == length);

                return(value);
            }
            catch (JsonException)
            {
                reader = restore;
                throw;
            }
            finally
            {
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);
            }
        }