/// <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; } }
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())); }
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); } } }
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); } } }
/// <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); }
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)); }
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); } } }
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); }
/// <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); }