private static TValue?ReadFromSpan <TValue>(ReadOnlySpan <char> json, JsonTypeInfo jsonTypeInfo) { jsonTypeInfo.EnsureConfigured(); byte[]? tempArray = null; // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold. Span <byte> utf8 = 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, utf8); utf8 = utf8.Slice(0, actualByteCount); return(ReadFromSpan <TValue>(utf8, jsonTypeInfo, actualByteCount)); } finally { if (tempArray != null) { utf8.Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } }
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(); } }
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; jsonTypeInfo.EnsureConfigured(); 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); }
/// <summary> /// Converts the <see cref="JsonNode"/> representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="node">The <see cref="JsonNode"/> to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// <typeparamref name="TValue" /> is not compatible with the JSON. /// </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>(this JsonNode?node, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(ReadNode <TValue>(node, jsonTypeInfo)); }
/// <summary> /// Converts the provided value into a <see cref="byte"/> array. /// </summary> /// <returns>A UTF-8 representation of the value.</returns> /// <param name="value">The value to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> public static byte[] SerializeToUtf8Bytes <TValue>(TValue value, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(WriteBytes(value, jsonTypeInfo)); }
/// <summary> /// Parses the text representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="json">JSON text to parse.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="json"/> is <see langword="null"/>. /// /// -or- /// /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// The JSON is invalid. /// /// -or- /// /// <typeparamref name="TValue" /> is not compatible with the JSON. /// /// -or- /// /// There is remaining data in the string beyond a single JSON value.</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>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 TValue?Deserialize <TValue>([StringSyntax(StringSyntaxAttribute.Json)] ReadOnlySpan <char> json, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(ReadFromSpan <TValue?>(json, jsonTypeInfo)); }
/// <summary> /// Parses the UTF-8 encoded text representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <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"> /// 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 is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(ReadFromSpan <TValue>(utf8Json, jsonTypeInfo)); }
/// <summary> /// Converts the provided value into a <see cref="JsonNode"/>. /// </summary> /// <typeparam name="TValue">The type of the value to serialize.</typeparam> /// <returns>A <see cref="JsonNode"/> representation of the value.</returns> /// <param name="value">The value to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> public static JsonNode?SerializeToNode <TValue>(TValue value, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(WriteNode(value, jsonTypeInfo)); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <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"> /// 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"/>, and <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static TValue?Deserialize <TValue>(ref Utf8JsonReader reader, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(Read <TValue>(ref reader, jsonTypeInfo)); }
/// <summary> /// Converts the <see cref="JsonElement"/> representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="element">The <see cref="JsonElement"/> to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// <typeparamref name="TValue" /> is not compatible with the JSON. /// </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>(this JsonElement element, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(ReadUsingMetadata <TValue>(element, jsonTypeInfo)); }
/// <summary> /// Converts the <see cref="JsonDocument"/> representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="document">The <see cref="JsonDocument"/> to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// /// -or- /// /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// <typeparamref name="TValue" /> is not compatible with the JSON. /// </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>(this JsonDocument document, JsonTypeInfo <TValue> jsonTypeInfo) { if (document is null) { ThrowHelper.ThrowArgumentNullException(nameof(document)); } if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(ReadDocument <TValue>(document, jsonTypeInfo)); }
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(); } }
/// <summary> /// Writes one JSON value (including objects or arrays) to the provided writer. /// </summary> /// <typeparam name="TValue">The type of the value to serialize.</typeparam> /// <param name="writer">The writer to write.</param> /// <param name="value">The value to convert and write.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="writer"/> or <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </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 void Serialize <TValue>(Utf8JsonWriter writer, TValue value, JsonTypeInfo <TValue> jsonTypeInfo) { if (writer is null) { ThrowHelper.ThrowArgumentNullException(nameof(writer)); } if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); WriteCore(writer, value, jsonTypeInfo); }
/// <summary> /// Reads the UTF-8 encoded text representing a single JSON value into a <typeparamref name="TValue"/>. /// The Stream will be read to completion. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="utf8Json">JSON data to parse.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <param name="cancellationToken"> /// The <see cref="System.Threading.CancellationToken"/> that can be used to cancel the read operation. /// </param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="utf8Json"/> or <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// 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 ValueTask <TValue?> DeserializeAsync <TValue>( Stream utf8Json, JsonTypeInfo <TValue> jsonTypeInfo, CancellationToken cancellationToken = default) { if (utf8Json is null) { ThrowHelper.ThrowArgumentNullException(nameof(utf8Json)); } if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } jsonTypeInfo.EnsureConfigured(); return(ReadFromStreamAsync <TValue>(utf8Json, jsonTypeInfo, cancellationToken)); }
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; jsonTypeInfo.EnsureConfigured(); 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(); } }
/// <summary> /// Same as GetTypeInfo but without validation and additional knobs. /// </summary> internal JsonTypeInfo GetTypeInfoInternal(Type type, bool ensureConfigured = true, bool resolveIfMutable = false) { JsonTypeInfo?typeInfo = null; if (IsImmutable) { typeInfo = GetCachingContext()?.GetOrAddJsonTypeInfo(type); if (ensureConfigured) { typeInfo?.EnsureConfigured(); } } else if (resolveIfMutable) { typeInfo = GetTypeInfoNoCaching(type); } if (typeInfo == null) { ThrowHelper.ThrowNotSupportedException_NoMetadataForType(type, TypeInfoResolver); } return(typeInfo); }
private static async Task WriteStreamAsync <TValue>( Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken) { JsonSerializerOptions options = jsonTypeInfo.Options; JsonWriterOptions writerOptions = options.GetWriterOptions(); using (var bufferWriter = new PooledByteBufferWriter(options.DefaultBufferSize)) using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) { WriteStack state = new WriteStack { CancellationToken = cancellationToken }; jsonTypeInfo.EnsureConfigured(); JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true, supportAsync: true); bool isFinalBlock; try { do { state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold); try { isFinalBlock = WriteCore(converter, writer, value, options, ref state); if (state.SuppressFlush) { Debug.Assert(!isFinalBlock); Debug.Assert(state.PendingTask is not null); state.SuppressFlush = false; } else { await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); bufferWriter.Clear(); } } finally { // Await any pending resumable converter tasks (currently these can only be IAsyncEnumerator.MoveNextAsync() tasks). // Note that pending tasks are always awaited, even if an exception has been thrown or the cancellation token has fired. if (state.PendingTask is not null) { try { await state.PendingTask.ConfigureAwait(false); } catch { // Exceptions should only be propagated by the resuming converter // TODO https://github.com/dotnet/runtime/issues/22144 } } // Dispose any pending async disposables (currently these can only be completed IAsyncEnumerators). if (state.CompletedAsyncDisposables?.Count > 0) { await state.DisposeCompletedAsyncDisposables().ConfigureAwait(false); } } } while (!isFinalBlock); } catch { // On exception, walk the WriteStack for any orphaned disposables and try to dispose them. await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false); throw; } } }
private static TValue?Read <TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo) { ReadStack state = default; jsonTypeInfo.EnsureConfigured(); 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(ref 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); } }