File: System\Text\Json\Reader\Utf8JsonReader.TryGet.cs
Web Access
Project: src\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
 
namespace System.Text.Json
{
    public ref partial struct Utf8JsonReader
    {
        /// <summary>
        /// Parses the current JSON token value from the source, unescaped, and transcoded as a <see cref="string"/>.
        /// </summary>
        /// <remarks>
        /// Returns <see langword="null" /> when <see cref="TokenType"/> is <see cref="JsonTokenType.Null"/>.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of the JSON token that is not a string
        /// (i.e. other than <see cref="JsonTokenType.String"/>, <see cref="JsonTokenType.PropertyName"/> or
        /// <see cref="JsonTokenType.Null"/>).
        /// <seealso cref="TokenType" />
        /// It will also throw when the JSON string contains invalid UTF-8 bytes, or invalid UTF-16 surrogates.
        /// </exception>
        public string? GetString()
        {
            if (TokenType == JsonTokenType.Null)
            {
                return null;
            }
 
            if (TokenType != JsonTokenType.String && TokenType != JsonTokenType.PropertyName)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 
            if (ValueIsEscaped)
            {
                return JsonReaderHelper.GetUnescapedString(span);
            }
 
            Debug.Assert(span.IndexOf(JsonConstants.BackSlash) == -1);
            return JsonReaderHelper.TranscodeHelper(span);
        }
 
        /// <summary>
        /// Copies the current JSON token value from the source, unescaped as a UTF-8 string to the destination buffer.
        /// </summary>
        /// <param name="utf8Destination">A buffer to write the unescaped UTF-8 bytes into.</param>
        /// <returns>The number of bytes written to <paramref name="utf8Destination"/>.</returns>
        /// <remarks>
        /// Unlike <see cref="GetString"/>, this method does not support <see cref="JsonTokenType.Null"/>.
        ///
        /// This method will throw <see cref="ArgumentException"/> if the destination buffer is too small to hold the unescaped value.
        /// An appropriately sized buffer can be determined by consulting the length of either <see cref="ValueSpan"/> or <see cref="ValueSequence"/>,
        /// since the unescaped result is always less than or equal to the length of the encoded strings.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of the JSON token that is not a string
        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>.
        /// <seealso cref="TokenType" />
        /// It will also throw when the JSON string contains invalid UTF-8 bytes, or invalid UTF-16 surrogates.
        /// </exception>
        /// <exception cref="ArgumentException">The destination buffer is too small to hold the unescaped value.</exception>
        public readonly int CopyString(Span<byte> utf8Destination)
        {
            if (_tokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(_tokenType);
            }
 
            return CopyValue(utf8Destination);
        }
 
        internal readonly int CopyValue(Span<byte> utf8Destination)
        {
            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName or JsonTokenType.Number);
            Debug.Assert(_tokenType != JsonTokenType.Number || !ValueIsEscaped, "Numbers can't contain escape characters.");
 
            int bytesWritten;
 
            if (ValueIsEscaped)
            {
                if (!TryCopyEscapedString(utf8Destination, out bytesWritten))
                {
                    utf8Destination.Slice(0, bytesWritten).Clear();
                    ThrowHelper.ThrowArgumentException_DestinationTooShort();
                }
            }
            else
            {
                if (HasValueSequence)
                {
                    ReadOnlySequence<byte> valueSequence = ValueSequence;
                    valueSequence.CopyTo(utf8Destination);
                    bytesWritten = (int)valueSequence.Length;
                }
                else
                {
                    ReadOnlySpan<byte> valueSpan = ValueSpan;
                    valueSpan.CopyTo(utf8Destination);
                    bytesWritten = valueSpan.Length;
                }
            }
 
            JsonReaderHelper.ValidateUtf8(utf8Destination.Slice(0, bytesWritten));
            return bytesWritten;
        }
 
        /// <summary>
        /// Copies the current JSON token value from the source, unescaped, and transcoded as a UTF-16 char buffer.
        /// </summary>
        /// <param name="destination">A buffer to write the transcoded UTF-16 characters into.</param>
        /// <returns>The number of characters written to <paramref name="destination"/>.</returns>
        /// <remarks>
        /// Unlike <see cref="GetString"/>, this method does not support <see cref="JsonTokenType.Null"/>.
        ///
        /// This method will throw <see cref="ArgumentException"/> if the destination buffer is too small to hold the unescaped value.
        /// An appropriately sized buffer can be determined by consulting the length of either <see cref="ValueSpan"/> or <see cref="ValueSequence"/>,
        /// since the unescaped result is always less than or equal to the length of the encoded strings.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of the JSON token that is not a string
        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>.
        /// <seealso cref="TokenType" />
        /// It will also throw when the JSON string contains invalid UTF-8 bytes, or invalid UTF-16 surrogates.
        /// </exception>
        /// <exception cref="ArgumentException">The destination buffer is too small to hold the unescaped value.</exception>
        public readonly int CopyString(Span<char> destination)
        {
            if (_tokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(_tokenType);
            }
 
            return CopyValue(destination);
        }
 
        internal readonly int CopyValue(Span<char> destination)
        {
            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName or JsonTokenType.Number);
            Debug.Assert(_tokenType != JsonTokenType.Number || !ValueIsEscaped, "Numbers can't contain escape characters.");
 
            scoped ReadOnlySpan<byte> unescapedSource;
            byte[]? rentedBuffer = null;
            int valueLength;
 
            if (ValueIsEscaped)
            {
                valueLength = ValueLength;
 
                Span<byte> unescapedBuffer = valueLength <= JsonConstants.StackallocByteThreshold ?
                    stackalloc byte[JsonConstants.StackallocByteThreshold] :
                    (rentedBuffer = ArrayPool<byte>.Shared.Rent(valueLength));
 
                bool success = TryCopyEscapedString(unescapedBuffer, out int bytesWritten);
                Debug.Assert(success);
                unescapedSource = unescapedBuffer.Slice(0, bytesWritten);
            }
            else
            {
                if (HasValueSequence)
                {
                    ReadOnlySequence<byte> valueSequence = ValueSequence;
                    valueLength = checked((int)valueSequence.Length);
 
                    Span<byte> intermediate = valueLength <= JsonConstants.StackallocByteThreshold ?
                        stackalloc byte[JsonConstants.StackallocByteThreshold] :
                        (rentedBuffer = ArrayPool<byte>.Shared.Rent(valueLength));
 
                    valueSequence.CopyTo(intermediate);
                    unescapedSource = intermediate.Slice(0, valueLength);
                }
                else
                {
                    unescapedSource = ValueSpan;
                }
            }
 
            int charsWritten = JsonReaderHelper.TranscodeHelper(unescapedSource, destination);
 
            if (rentedBuffer != null)
            {
                new Span<byte>(rentedBuffer, 0, unescapedSource.Length).Clear();
                ArrayPool<byte>.Shared.Return(rentedBuffer);
            }
 
            return charsWritten;
        }
 
        private readonly bool TryCopyEscapedString(Span<byte> destination, out int bytesWritten)
        {
            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName);
            Debug.Assert(ValueIsEscaped);
 
            byte[]? rentedBuffer = null;
            scoped ReadOnlySpan<byte> source;
 
            if (HasValueSequence)
            {
                ReadOnlySequence<byte> valueSequence = ValueSequence;
                int sequenceLength = checked((int)valueSequence.Length);
 
                Span<byte> intermediate = sequenceLength <= JsonConstants.StackallocByteThreshold ?
                    stackalloc byte[JsonConstants.StackallocByteThreshold] :
                    (rentedBuffer = ArrayPool<byte>.Shared.Rent(sequenceLength));
 
                valueSequence.CopyTo(intermediate);
                source = intermediate.Slice(0, sequenceLength);
            }
            else
            {
                source = ValueSpan;
            }
 
            bool success = JsonReaderHelper.TryUnescape(source, destination, out bytesWritten);
 
            if (rentedBuffer != null)
            {
                new Span<byte>(rentedBuffer, 0, source.Length).Clear();
                ArrayPool<byte>.Shared.Return(rentedBuffer);
            }
 
            Debug.Assert(bytesWritten < source.Length, "source buffer must contain at least one escape sequence");
            return success;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a comment, transcoded as a <see cref="string"/>.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of the JSON token that is not a comment.
        /// <seealso cref="TokenType" />
        /// </exception>
        public string GetComment()
        {
            if (TokenType != JsonTokenType.Comment)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedComment(TokenType);
            }
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return JsonReaderHelper.TranscodeHelper(span);
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="bool"/>.
        /// Returns <see langword="true"/> if the TokenType is JsonTokenType.True and <see langword="false"/> if the TokenType is JsonTokenType.False.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a boolean (i.e. <see cref="JsonTokenType.True"/> or <see cref="JsonTokenType.False"/>).
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool GetBoolean()
        {
            JsonTokenType type = TokenType;
            if (type == JsonTokenType.True)
            {
                Debug.Assert((HasValueSequence ? ValueSequence.ToArray() : ValueSpan).Length == 4);
                return true;
            }
            else if (type != JsonTokenType.False)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedBoolean(TokenType);
                Debug.Fail("Throw helper should have thrown an exception.");
            }
 
            Debug.Assert((HasValueSequence ? ValueSequence.ToArray() : ValueSpan).Length == 5);
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source and decodes the Base64 encoded JSON string as bytes.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// The JSON string contains data outside of the expected Base64 range, or if it contains invalid/more than two padding characters,
        /// or is incomplete (i.e. the JSON string length is not a multiple of 4).
        /// </exception>
        public byte[] GetBytesFromBase64()
        {
            if (!TryGetBytesFromBase64(out byte[]? value))
            {
                ThrowHelper.ThrowFormatException(DataType.Base64String);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="byte"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="byte"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="byte.MinValue"/> or greater
        /// than <see cref="byte.MaxValue"/>.
        /// </exception>
        public byte GetByte()
        {
            if (!TryGetByte(out byte value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Byte);
            }
            return value;
        }
 
        internal byte GetByteWithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetByteCore(out byte value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.Byte);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as an <see cref="sbyte"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to an <see cref="sbyte"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="sbyte.MinValue"/> or greater
        /// than <see cref="sbyte.MaxValue"/>.
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public sbyte GetSByte()
        {
            if (!TryGetSByte(out sbyte value))
            {
                ThrowHelper.ThrowFormatException(NumericType.SByte);
            }
            return value;
        }
 
        internal sbyte GetSByteWithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetSByteCore(out sbyte value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.SByte);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="short"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="short"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="short.MinValue"/> or greater
        /// than <see cref="short.MaxValue"/>.
        /// </exception>
        public short GetInt16()
        {
            if (!TryGetInt16(out short value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Int16);
            }
            return value;
        }
 
        internal short GetInt16WithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetInt16Core(out short value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.Int16);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as an <see cref="int"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to an <see cref="int"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="int.MinValue"/> or greater
        /// than <see cref="int.MaxValue"/>.
        /// </exception>
        public int GetInt32()
        {
            if (!TryGetInt32(out int value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Int32);
            }
            return value;
        }
 
        internal int GetInt32WithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetInt32Core(out int value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.Int32);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="long"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="long"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="long.MinValue"/> or greater
        /// than <see cref="long.MaxValue"/>.
        /// </exception>
        public long GetInt64()
        {
            if (!TryGetInt64(out long value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Int64);
            }
            return value;
        }
 
        internal long GetInt64WithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetInt64Core(out long value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.Int64);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="ushort"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="ushort"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="ushort.MinValue"/> or greater
        /// than <see cref="ushort.MaxValue"/>.
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public ushort GetUInt16()
        {
            if (!TryGetUInt16(out ushort value))
            {
                ThrowHelper.ThrowFormatException(NumericType.UInt16);
            }
            return value;
        }
 
        internal ushort GetUInt16WithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetUInt16Core(out ushort value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.UInt16);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="uint"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="uint"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="uint.MinValue"/> or greater
        /// than <see cref="uint.MaxValue"/>.
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public uint GetUInt32()
        {
            if (!TryGetUInt32(out uint value))
            {
                ThrowHelper.ThrowFormatException(NumericType.UInt32);
            }
            return value;
        }
 
        internal uint GetUInt32WithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetUInt32Core(out uint value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.UInt32);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="ulong"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="ulong"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal or
        /// is written in scientific notation) or, it represents a number less than <see cref="ulong.MinValue"/> or greater
        /// than <see cref="ulong.MaxValue"/>.
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public ulong GetUInt64()
        {
            if (!TryGetUInt64(out ulong value))
            {
                ThrowHelper.ThrowFormatException(NumericType.UInt64);
            }
            return value;
        }
 
        internal ulong GetUInt64WithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetUInt64Core(out ulong value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.UInt64);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="float"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="float"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// On any framework that is not .NET Core 3.0 or higher, thrown if the JSON token value represents a number less than <see cref="float.MinValue"/> or greater
        /// than <see cref="float.MaxValue"/>.
        /// </exception>
        public float GetSingle()
        {
            if (!TryGetSingle(out float value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Single);
            }
            return value;
        }
 
        internal float GetSingleWithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
 
            if (JsonReaderHelper.TryGetFloatingPointConstant(span, out float value))
            {
                return value;
            }
 
            // NETCOREAPP implementation of the TryParse method above permits case-insensitive variants of the
            // float constants "NaN", "Infinity", "-Infinity". This differs from the NETFRAMEWORK implementation.
            // The following logic reconciles the two implementations to enforce consistent behavior.
            if (!(Utf8Parser.TryParse(span, out value, out int bytesConsumed)
                  && span.Length == bytesConsumed
                  && JsonHelpers.IsFinite(value)))
            {
                ThrowHelper.ThrowFormatException(NumericType.Single);
            }
 
            return value;
        }
 
        internal float GetSingleFloatingPointConstant()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
 
            if (!JsonReaderHelper.TryGetFloatingPointConstant(span, out float value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Single);
            }
 
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="double"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="double"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// On any framework that is not .NET Core 3.0 or higher, thrown if the JSON token value represents a number less than <see cref="double.MinValue"/> or greater
        /// than <see cref="double.MaxValue"/>.
        /// </exception>
        public double GetDouble()
        {
            if (!TryGetDouble(out double value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Double);
            }
            return value;
        }
 
        internal double GetDoubleWithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
 
            if (JsonReaderHelper.TryGetFloatingPointConstant(span, out double value))
            {
                return value;
            }
 
            // NETCOREAPP implementation of the TryParse method above permits case-insensitive variants of the
            // float constants "NaN", "Infinity", "-Infinity". This differs from the NETFRAMEWORK implementation.
            // The following logic reconciles the two implementations to enforce consistent behavior.
            if (!(Utf8Parser.TryParse(span, out value, out int bytesConsumed)
                  && span.Length == bytesConsumed
                  && JsonHelpers.IsFinite(value)))
            {
                ThrowHelper.ThrowFormatException(NumericType.Double);
            }
 
            return value;
        }
 
        internal double GetDoubleFloatingPointConstant()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
 
            if (!JsonReaderHelper.TryGetFloatingPointConstant(span, out double value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Double);
            }
 
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="decimal"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="decimal"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value represents a number less than <see cref="decimal.MinValue"/> or greater
        /// than <see cref="decimal.MaxValue"/>.
        /// </exception>
        public decimal GetDecimal()
        {
            if (!TryGetDecimal(out decimal value))
            {
                ThrowHelper.ThrowFormatException(NumericType.Decimal);
            }
            return value;
        }
 
        internal decimal GetDecimalWithQuotes()
        {
            ReadOnlySpan<byte> span = GetUnescapedSpan();
            if (!TryGetDecimalCore(out decimal value, span))
            {
                ThrowHelper.ThrowFormatException(NumericType.Decimal);
            }
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="DateTime"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="DateTime"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is of an unsupported format. Only a subset of ISO 8601 formats are supported.
        /// </exception>
        public DateTime GetDateTime()
        {
            if (!TryGetDateTime(out DateTime value))
            {
                ThrowHelper.ThrowFormatException(DataType.DateTime);
            }
 
            return value;
        }
 
        internal DateTime GetDateTimeNoValidation()
        {
            if (!TryGetDateTimeCore(out DateTime value))
            {
                ThrowHelper.ThrowFormatException(DataType.DateTime);
            }
 
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="DateTimeOffset"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="DateTimeOffset"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is of an unsupported format. Only a subset of ISO 8601 formats are supported.
        /// </exception>
        public DateTimeOffset GetDateTimeOffset()
        {
            if (!TryGetDateTimeOffset(out DateTimeOffset value))
            {
                ThrowHelper.ThrowFormatException(DataType.DateTimeOffset);
            }
 
            return value;
        }
 
        internal DateTimeOffset GetDateTimeOffsetNoValidation()
        {
            if (!TryGetDateTimeOffsetCore(out DateTimeOffset value))
            {
                ThrowHelper.ThrowFormatException(DataType.DateTimeOffset);
            }
 
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="Guid"/>.
        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="Guid"/>
        /// value.
        /// Throws exceptions otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        /// <exception cref="FormatException">
        /// Thrown if the JSON token value is of an unsupported format for a Guid.
        /// </exception>
        public Guid GetGuid()
        {
            if (!TryGetGuid(out Guid value))
            {
                ThrowHelper.ThrowFormatException(DataType.Guid);
            }
 
            return value;
        }
 
        internal Guid GetGuidNoValidation()
        {
            if (!TryGetGuidCore(out Guid value))
            {
                ThrowHelper.ThrowFormatException(DataType.Guid);
            }
 
            return value;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source and decodes the Base64 encoded JSON string as bytes.
        /// Returns <see langword="true"/> if the entire token value is encoded as valid Base64 text and can be successfully
        /// decoded to bytes.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetBytesFromBase64([NotNullWhen(true)] out byte[]? value)
        {
            if (TokenType != JsonTokenType.String)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 
            if (ValueIsEscaped)
            {
                return JsonReaderHelper.TryGetUnescapedBase64Bytes(span, out value);
            }
 
            Debug.Assert(span.IndexOf(JsonConstants.BackSlash) == -1);
            return JsonReaderHelper.TryDecodeBase64(span, out value);
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="byte"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="byte"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetByte(out byte value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetByteCore(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetByteCore(out byte value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out byte tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as an <see cref="sbyte"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to an <see cref="sbyte"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public bool TryGetSByte(out sbyte value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetSByteCore(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetSByteCore(out sbyte value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out sbyte tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="short"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="short"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetInt16(out short value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetInt16Core(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetInt16Core(out short value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out short tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as an <see cref="int"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to an <see cref="int"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetInt32(out int value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetInt32Core(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetInt32Core(out int value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out int tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="long"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="long"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetInt64(out long value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetInt64Core(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetInt64Core(out long value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out long tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="ushort"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="ushort"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public bool TryGetUInt16(out ushort value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetUInt16Core(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetUInt16Core(out ushort value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out ushort tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="uint"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="uint"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public bool TryGetUInt32(out uint value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetUInt32Core(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetUInt32Core(out uint value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out uint tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="ulong"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="ulong"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        [System.CLSCompliantAttribute(false)]
        public bool TryGetUInt64(out ulong value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetUInt64Core(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetUInt64Core(out ulong value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out ulong tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="float"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="float"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetSingle(out float value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 
            if (Utf8Parser.TryParse(span, out float tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="double"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="double"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetDouble(out double value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 
            if (Utf8Parser.TryParse(span, out double tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="decimal"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="decimal"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetDecimal(out decimal value)
        {
            if (TokenType != JsonTokenType.Number)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
            }
 
            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
            return TryGetDecimalCore(out value, span);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static bool TryGetDecimalCore(out decimal value, ReadOnlySpan<byte> span)
        {
            if (Utf8Parser.TryParse(span, out decimal tmp, out int bytesConsumed)
                && span.Length == bytesConsumed)
            {
                value = tmp;
                return true;
            }
 
            value = 0;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="DateTime"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="DateTime"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetDateTime(out DateTime value)
        {
            if (TokenType != JsonTokenType.String)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
            }
 
            return TryGetDateTimeCore(out value);
        }
 
        internal bool TryGetDateTimeCore(out DateTime value)
        {
            scoped ReadOnlySpan<byte> span;
 
            if (HasValueSequence)
            {
                long sequenceLength = ValueSequence.Length;
                if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength))
                {
                    value = default;
                    return false;
                }
 
                Span<byte> stackSpan = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
                ValueSequence.CopyTo(stackSpan);
                span = stackSpan.Slice(0, (int)sequenceLength);
            }
            else
            {
                if (!JsonHelpers.IsInRangeInclusive(ValueSpan.Length, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength))
                {
                    value = default;
                    return false;
                }
 
                span = ValueSpan;
            }
 
            if (ValueIsEscaped)
            {
                return JsonReaderHelper.TryGetEscapedDateTime(span, out value);
            }
 
            Debug.Assert(span.IndexOf(JsonConstants.BackSlash) == -1);
 
            if (JsonHelpers.TryParseAsISO(span, out DateTime tmp))
            {
                value = tmp;
                return true;
            }
 
            value = default;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="DateTimeOffset"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="DateTimeOffset"/> value.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetDateTimeOffset(out DateTimeOffset value)
        {
            if (TokenType != JsonTokenType.String)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
            }
 
            return TryGetDateTimeOffsetCore(out value);
        }
 
        internal bool TryGetDateTimeOffsetCore(out DateTimeOffset value)
        {
            scoped ReadOnlySpan<byte> span;
 
            if (HasValueSequence)
            {
                long sequenceLength = ValueSequence.Length;
                if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength))
                {
                    value = default;
                    return false;
                }
 
                Span<byte> stackSpan = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
                ValueSequence.CopyTo(stackSpan);
                span = stackSpan.Slice(0, (int)sequenceLength);
            }
            else
            {
                if (!JsonHelpers.IsInRangeInclusive(ValueSpan.Length, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength))
                {
                    value = default;
                    return false;
                }
 
                span = ValueSpan;
            }
 
            if (ValueIsEscaped)
            {
                return JsonReaderHelper.TryGetEscapedDateTimeOffset(span, out value);
            }
 
            Debug.Assert(span.IndexOf(JsonConstants.BackSlash) == -1);
 
            if (JsonHelpers.TryParseAsISO(span, out DateTimeOffset tmp))
            {
                value = tmp;
                return true;
            }
 
            value = default;
            return false;
        }
 
        /// <summary>
        /// Parses the current JSON token value from the source as a <see cref="Guid"/>.
        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
        /// parsed to a <see cref="Guid"/> value. Only supports <see cref="Guid"/> values with hyphens
        /// and without any surrounding decorations.
        /// Returns <see langword="false"/> otherwise.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
        /// <seealso cref="TokenType" />
        /// </exception>
        public bool TryGetGuid(out Guid value)
        {
            if (TokenType != JsonTokenType.String)
            {
                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
            }
 
            return TryGetGuidCore(out value);
        }
 
        internal bool TryGetGuidCore(out Guid value)
        {
            scoped ReadOnlySpan<byte> span;
 
            if (HasValueSequence)
            {
                long sequenceLength = ValueSequence.Length;
                if (sequenceLength > JsonConstants.MaximumEscapedGuidLength)
                {
                    value = default;
                    return false;
                }
 
                Span<byte> stackSpan = stackalloc byte[JsonConstants.MaximumEscapedGuidLength];
                ValueSequence.CopyTo(stackSpan);
                span = stackSpan.Slice(0, (int)sequenceLength);
            }
            else
            {
                if (ValueSpan.Length > JsonConstants.MaximumEscapedGuidLength)
                {
                    value = default;
                    return false;
                }
 
                span = ValueSpan;
            }
 
            if (ValueIsEscaped)
            {
                return JsonReaderHelper.TryGetEscapedGuid(span, out value);
            }
 
            Debug.Assert(span.IndexOf(JsonConstants.BackSlash) == -1);
 
            if (span.Length == JsonConstants.MaximumFormatGuidLength
                && Utf8Parser.TryParse(span, out Guid tmp, out _, 'D'))
            {
                value = tmp;
                return true;
            }
 
            value = default;
            return false;
        }
    }
}