Exemplo n.º 1
0
        /// <summary>
        ///   Parses text representing a single JSON value into a JsonDocument.
        /// </summary>
        /// <remarks>
        ///   The <see cref="ReadOnlyMemory{T}"/> value may be used for the entire lifetime of the
        ///   JsonDocument object, and the caller must ensure that the data therein does not change during
        ///   the object lifetime.
        /// </remarks>
        /// <param name="json">JSON text to parse.</param>
        /// <param name="options">Options to control the reader behavior during parsing.</param>
        /// <returns>
        ///   A JsonDocument representation of the JSON value.
        /// </returns>
        /// <exception cref="JsonException">
        ///   <paramref name="json"/> does not represent a valid single JSON value.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="options"/> contains unsupported options.
        /// </exception>
        public static JsonDocument Parse([StringSyntax(StringSyntaxAttribute.Json)] ReadOnlyMemory <char> json, JsonDocumentOptions options = default)
        {
            ReadOnlySpan <char> jsonChars = json.Span;
            int expectedByteCount         = JsonReaderHelper.GetUtf8ByteCount(jsonChars);

            byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount);

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(jsonChars, utf8Bytes);
                Debug.Assert(expectedByteCount == actualByteCount);

                return(Parse(
                           utf8Bytes.AsMemory(0, actualByteCount),
                           options.GetReaderOptions(),
                           utf8Bytes));
            }
            catch
            {
                // Holds document content, clear it before returning it.
                utf8Bytes.AsSpan(0, expectedByteCount).Clear();
                ArrayPool <byte> .Shared.Return(utf8Bytes);

                throw;
            }
        }
Exemplo n.º 2
0
        internal static JsonDocument ParseValue(ReadOnlyMemory <char> json, JsonDocumentOptions options)
        {
            ReadOnlySpan <char> jsonChars = json.Span;
            int expectedByteCount         = JsonReaderHelper.GetUtf8ByteCount(jsonChars);

            byte[] owned;
            byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount);

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(jsonChars, utf8Bytes);
                Debug.Assert(expectedByteCount == actualByteCount);

                owned = new byte[actualByteCount];
                Buffer.BlockCopy(utf8Bytes, 0, owned, 0, actualByteCount);
            }
            finally
            {
                // Holds document content, clear it before returning it.
                utf8Bytes.AsSpan(0, expectedByteCount).Clear();
                ArrayPool <byte> .Shared.Return(utf8Bytes);
            }

            return(ParseUnrented(owned.AsMemory(), options.GetReaderOptions()));
        }
Exemplo n.º 3
0
        private static TValue?ReadUsingMetadata <TValue>(ReadOnlySpan <char> json, JsonTypeInfo jsonTypeInfo)
        {
            const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024;

            byte[]? tempArray = null;

            // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
            Span <byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
                               // Use a pooled alloc.
                               tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) :
                               // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation)
                               // and by using a normal alloc we can avoid the Clear().
                                           new byte[JsonReaderHelper.GetUtf8ByteCount(json)];

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8);
                utf8 = utf8.Slice(0, actualByteCount);
                return(ReadUsingMetadata <TValue>(utf8, jsonTypeInfo, actualByteCount));
            }
            finally
            {
                if (tempArray != null)
                {
                    utf8.Clear();
                    ArrayPool <byte> .Shared.Return(tempArray);
                }
            }
        }
Exemplo n.º 4
0
        private void TranscodeAndWriteRawValue(ReadOnlySpan <char> json, bool skipInputValidation)
        {
            if (json.Length > JsonConstants.MaxUtf16RawValueLength)
            {
                ThrowHelper.ThrowArgumentException_ValueTooLarge(json.Length);
            }

            byte[]? tempArray = null;

            // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
            Span <byte> utf8Json = json.Length <= (JsonConstants.ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
                                   // Use a pooled alloc.
                                   tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) :
                                   // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation)
                                   // and by using a normal alloc we can avoid the Clear().
                                               new byte[JsonReaderHelper.GetUtf8ByteCount(json)];

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8Json);
                utf8Json = utf8Json.Slice(0, actualByteCount);
                WriteRawValueCore(utf8Json, skipInputValidation);
            }
            finally
            {
                if (tempArray != null)
                {
                    utf8Json.Clear();
                    ArrayPool <byte> .Shared.Return(tempArray);
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Parse the 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="json">JSON text to parse.</param>
        /// <param name="returnType">The type of the object to convert to and return.</param>
        /// <param name="options">Options to control the behavior during parsing.</param>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="json"/> or <paramref name="returnType"/> is null.
        /// </exception>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// the <paramref name="returnType"/> is not compatible with the JSON,
        /// or when there is remaining data in the Stream.
        /// </exception>
        /// <remarks>Using a <see cref="System.String"/> is not as efficient as using the
        /// UTF-8 methods since the implementation natively uses UTF-8.
        /// </remarks>
        public static object Deserialize(string json, Type returnType, JsonSerializerOptions options = null)
        {
            const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024;

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

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

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

            object result;

            byte[] tempArray = null;

            // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
            Span <byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
                               // Use a pooled alloc.
                               tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) :
                               // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation)
                               // and by using a normal alloc we can avoid the Clear().
                                           new byte[JsonReaderHelper.GetUtf8ByteCount(json.AsSpan())];

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(json.AsSpan(), utf8);
                utf8 = utf8.Slice(0, actualByteCount);

                var readerState = new JsonReaderState(options.GetReaderOptions());
                var reader      = new Utf8JsonReader(utf8, isFinalBlock: true, readerState);
                result = ReadCore(returnType, options, ref reader);

                // The reader should have thrown if we have remaining bytes.
                Debug.Assert(reader.BytesConsumed == actualByteCount);
            }
            finally
            {
                if (tempArray != null)
                {
                    utf8.Clear();
                    ArrayPool <byte> .Shared.Return(tempArray);
                }
            }

            return(result);
        }
Exemplo n.º 6
0
        public static bool TryParseAsISO(ReadOnlySpan <char> source, out DateTimeOffset value)
        {
            if (!IsValidDateTimeOffsetParseLength(source.Length))
            {
                value = default;
                return(false);
            }

            int length = JsonReaderHelper.GetUtf8ByteCount(source);

            Span <byte> bytes = length <= JsonConstants.StackallocThreshold
                ? stackalloc byte[JsonConstants.StackallocThreshold]
                : new byte[length];

            JsonReaderHelper.GetUtf8FromText(source, bytes);

            return(TryParseAsISO(bytes.Slice(0, length), out value));
        }
Exemplo n.º 7
0
        private static TValue?Deserialize <TValue>(
            JsonConverter jsonConverter,
            ReadOnlySpan <char> json,
            JsonSerializerOptions options,
            ref ReadStack state)
        {
            const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024;

            byte[]? tempArray = null;

            // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
            Span <byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
                               // Use a pooled alloc.
                               tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) :
                               // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation)
                               // and by using a normal alloc we can avoid the Clear().
                                           new byte[JsonReaderHelper.GetUtf8ByteCount(json)];

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8);
                utf8 = utf8.Slice(0, actualByteCount);

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

                TValue?value = ReadCore <TValue>(jsonConverter, ref reader, options, ref state);

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

                return(value);
            }
            finally
            {
                if (tempArray != null)
                {
                    utf8.Clear();
                    ArrayPool <byte> .Shared.Return(tempArray);
                }
            }
        }
Exemplo n.º 8
0
        private static JsonEncodedText TranscodeAndEncode(ReadOnlySpan <char> value)
        {
            JsonWriterHelper.ValidateValue(value);

            int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(value);

            byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount);

            JsonEncodedText encodedText;

            // Since GetUtf8ByteCount above already throws on invalid input, the transcoding
            // to UTF-8 is guaranteed to succeed here. Therefore, there's no need for a try-catch-finally block.
            int actualByteCount = JsonReaderHelper.GetUtf8FromText(value, utf8Bytes);

            Debug.Assert(expectedByteCount == actualByteCount);

            encodedText = EncodeHelper(utf8Bytes.AsSpan(0, actualByteCount));

            // On the basis that this is user data, go ahead and clear it.
            utf8Bytes.AsSpan(0, expectedByteCount).Clear();
            ArrayPool <byte> .Shared.Return(utf8Bytes);

            return(encodedText);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Parse the 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="json">JSON text to parse.</param>
        /// <param name="returnType">The type of the object to convert to and return.</param>
        /// <param name="options">Options to control the behavior during parsing.</param>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="json"/> or <paramref name="returnType"/> is null.
        /// </exception>
        /// <exception cref="JsonException">
        /// Thrown when the JSON is invalid,
        /// the <paramref name="returnType"/> is not compatible with the JSON,
        /// or when there is remaining data in the Stream.
        /// </exception>
        /// <remarks>Using a <see cref="string"/> is not as efficient as using the
        /// UTF-8 methods since the implementation natively uses UTF-8.
        /// </remarks>
        public static object Deserialize(string json, Type returnType, JsonSerializerOptions options = null)
        {
            if (json == null)
            {
                throw new ArgumentNullException(nameof(json));
            }

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

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

            object result;

            byte[] tempArray = null;

            int maxBytes;

            // For performance, avoid asking for the actual byte count unless necessary.
            if (json.Length > MaxArrayLengthBeforeCalculatingSize)
            {
                // Get the actual byte count in order to handle large input.
                maxBytes = JsonReaderHelper.GetUtf8ByteCount(json.AsSpan());
            }
            else
            {
                maxBytes = json.Length * JsonConstants.MaxExpansionFactorWhileTranscoding;
            }

            Span <byte> utf8 = maxBytes <= JsonConstants.StackallocThreshold ?
                               stackalloc byte[maxBytes] :
                               (tempArray = ArrayPool <byte> .Shared.Rent(maxBytes));

            try
            {
                int actualByteCount = JsonReaderHelper.GetUtf8FromText(json.AsSpan(), utf8);
                utf8 = utf8.Slice(0, actualByteCount);

                var readerState = new JsonReaderState(options.GetReaderOptions());
                var reader      = new Utf8JsonReader(utf8, isFinalBlock: true, readerState);
                result = ReadCore(returnType, options, ref reader);

                if (reader.BytesConsumed != actualByteCount)
                {
                    ThrowHelper.ThrowJsonException_DeserializeDataRemaining(
                        actualByteCount, actualByteCount - reader.BytesConsumed);
                }
            }
            finally
            {
                if (tempArray != null)
                {
                    utf8.Clear();
                    ArrayPool <byte> .Shared.Return(tempArray);
                }
            }

            return(result);
        }