2021-03-29 14:54:12 +08:00
#region License
// Copyright (c) 2007 James Newton-King
/ /
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
/ /
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
/ /
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
# endregion
#if HAVE_ASYNC
using System ;
using System.Globalization ;
using System.Threading ;
#if HAVE_BIG_INTEGER
using System.Numerics ;
# endif
using System.Threading.Tasks ;
using LC.Newtonsoft.Json.Serialization ;
using LC.Newtonsoft.Json.Utilities ;
namespace LC.Newtonsoft.Json
{
public partial class JsonTextReader
{
// It's not safe to perform the async methods here in a derived class as if the synchronous equivalent
// has been overriden then the asychronous method will no longer be doing the same operation
#if HAVE_ASYNC // Double-check this isn't included inappropriately.
private readonly bool _safeAsync ;
# endif
/// <summary>
/// Asynchronously reads the next JSON token from the source.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns <c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < bool > ReadAsync ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsync ( cancellationToken ) : base . ReadAsync ( cancellationToken ) ;
}
internal Task < bool > DoReadAsync ( CancellationToken cancellationToken )
{
EnsureBuffer ( ) ;
while ( true )
{
switch ( _currentState )
{
case State . Start :
case State . Property :
case State . Array :
case State . ArrayStart :
case State . Constructor :
case State . ConstructorStart :
return ParseValueAsync ( cancellationToken ) ;
case State . Object :
case State . ObjectStart :
return ParseObjectAsync ( cancellationToken ) ;
case State . PostValue :
Task < bool > task = ParsePostValueAsync ( false , cancellationToken ) ;
if ( task . IsCompletedSucessfully ( ) )
{
if ( task . Result )
{
return AsyncUtils . True ;
}
}
else
{
return DoReadAsync ( task , cancellationToken ) ;
}
break ;
case State . Finished :
return ReadFromFinishedAsync ( cancellationToken ) ;
default :
throw JsonReaderException . Create ( this , "Unexpected state: {0}." . FormatWith ( CultureInfo . InvariantCulture , CurrentState ) ) ;
}
}
}
private async Task < bool > DoReadAsync ( Task < bool > task , CancellationToken cancellationToken )
{
bool result = await task . ConfigureAwait ( false ) ;
if ( result )
{
return true ;
}
return await DoReadAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
}
private async Task < bool > ParsePostValueAsync ( bool ignoreComments , CancellationToken cancellationToken )
{
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( false , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
_currentState = State . Finished ;
return false ;
}
}
else
{
_charPos + + ;
}
break ;
case '}' :
_charPos + + ;
SetToken ( JsonToken . EndObject ) ;
return true ;
case ']' :
_charPos + + ;
SetToken ( JsonToken . EndArray ) ;
return true ;
case ')' :
_charPos + + ;
SetToken ( JsonToken . EndConstructor ) ;
return true ;
case '/' :
await ParseCommentAsync ( ! ignoreComments , cancellationToken ) . ConfigureAwait ( false ) ;
if ( ! ignoreComments )
{
return true ;
}
break ;
case ',' :
_charPos + + ;
// finished parsing
SetStateBasedOnCurrent ( ) ;
return false ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
default :
if ( char . IsWhiteSpace ( currentChar ) )
{
// eat
_charPos + + ;
}
else
{
// handle multiple content without comma delimiter
if ( SupportMultipleContent & & Depth = = 0 )
{
SetStateBasedOnCurrent ( ) ;
return false ;
}
throw JsonReaderException . Create ( this , "After parsing a value an unexpected character was encountered: {0}." . FormatWith ( CultureInfo . InvariantCulture , currentChar ) ) ;
}
break ;
}
}
}
private async Task < bool > ReadFromFinishedAsync ( CancellationToken cancellationToken )
{
if ( await EnsureCharsAsync ( 0 , false , cancellationToken ) . ConfigureAwait ( false ) )
{
await EatWhitespaceAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( _isEndOfFile )
{
SetToken ( JsonToken . None ) ;
return false ;
}
if ( _chars [ _charPos ] = = '/' )
{
await ParseCommentAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
}
throw JsonReaderException . Create ( this , "Additional text encountered after finished reading JSON content: {0}." . FormatWith ( CultureInfo . InvariantCulture , _chars [ _charPos ] ) ) ;
}
SetToken ( JsonToken . None ) ;
return false ;
}
private Task < int > ReadDataAsync ( bool append , CancellationToken cancellationToken )
{
return ReadDataAsync ( append , 0 , cancellationToken ) ;
}
private async Task < int > ReadDataAsync ( bool append , int charsRequired , CancellationToken cancellationToken )
{
if ( _isEndOfFile )
{
return 0 ;
}
PrepareBufferForReadData ( append , charsRequired ) ;
int charsRead = await _reader . ReadAsync ( _chars , _charsUsed , _chars . Length - _charsUsed - 1 , cancellationToken ) . ConfigureAwait ( false ) ;
_charsUsed + = charsRead ;
if ( charsRead = = 0 )
{
_isEndOfFile = true ;
}
_chars [ _charsUsed ] = '\0' ;
return charsRead ;
}
private async Task < bool > ParseValueAsync ( CancellationToken cancellationToken )
{
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( false , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
return false ;
}
}
else
{
_charPos + + ;
}
break ;
case '"' :
case '\'' :
await ParseStringAsync ( currentChar , ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case 't' :
await ParseTrueAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case 'f' :
await ParseFalseAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case 'n' :
if ( await EnsureCharsAsync ( 1 , true , cancellationToken ) . ConfigureAwait ( false ) )
{
switch ( _chars [ _charPos + 1 ] )
{
case 'u' :
await ParseNullAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case 'e' :
await ParseConstructorAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
break ;
default :
throw CreateUnexpectedCharacterException ( _chars [ _charPos ] ) ;
}
}
else
{
_charPos + + ;
throw CreateUnexpectedEndException ( ) ;
}
return true ;
case 'N' :
await ParseNumberNaNAsync ( ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case 'I' :
await ParseNumberPositiveInfinityAsync ( ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case '-' :
if ( await EnsureCharsAsync ( 1 , true , cancellationToken ) . ConfigureAwait ( false ) & & _chars [ _charPos + 1 ] = = 'I' )
{
await ParseNumberNegativeInfinityAsync ( ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
}
else
{
await ParseNumberAsync ( ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
}
return true ;
case '/' :
await ParseCommentAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case 'u' :
await ParseUndefinedAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case '{' :
_charPos + + ;
SetToken ( JsonToken . StartObject ) ;
return true ;
case '[' :
_charPos + + ;
SetToken ( JsonToken . StartArray ) ;
return true ;
case ']' :
_charPos + + ;
SetToken ( JsonToken . EndArray ) ;
return true ;
case ',' :
// don't increment position, the next call to read will handle comma
// this is done to handle multiple empty comma values
SetToken ( JsonToken . Undefined ) ;
return true ;
case ')' :
_charPos + + ;
SetToken ( JsonToken . EndConstructor ) ;
return true ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
default :
if ( char . IsWhiteSpace ( currentChar ) )
{
// eat
_charPos + + ;
break ;
}
if ( char . IsNumber ( currentChar ) | | currentChar = = '-' | | currentChar = = '.' )
{
await ParseNumberAsync ( ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
}
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
}
}
private async Task ReadStringIntoBufferAsync ( char quote , CancellationToken cancellationToken )
{
int charPos = _charPos ;
int initialPosition = _charPos ;
int lastWritePosition = _charPos ;
_stringBuffer . Position = 0 ;
while ( true )
{
switch ( _chars [ charPos + + ] )
{
case '\0' :
if ( _charsUsed = = charPos - 1 )
{
charPos - - ;
if ( await ReadDataAsync ( true , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
_charPos = charPos ;
throw JsonReaderException . Create ( this , "Unterminated string. Expected delimiter: {0}." . FormatWith ( CultureInfo . InvariantCulture , quote ) ) ;
}
}
break ;
case '\\' :
_charPos = charPos ;
if ( ! await EnsureCharsAsync ( 0 , true , cancellationToken ) . ConfigureAwait ( false ) )
{
throw JsonReaderException . Create ( this , "Unterminated string. Expected delimiter: {0}." . FormatWith ( CultureInfo . InvariantCulture , quote ) ) ;
}
// start of escape sequence
int escapeStartPos = charPos - 1 ;
char currentChar = _chars [ charPos ] ;
charPos + + ;
char writeChar ;
switch ( currentChar )
{
case 'b' :
writeChar = '\b' ;
break ;
case 't' :
writeChar = '\t' ;
break ;
case 'n' :
writeChar = '\n' ;
break ;
case 'f' :
writeChar = '\f' ;
break ;
case 'r' :
writeChar = '\r' ;
break ;
case '\\' :
writeChar = '\\' ;
break ;
case '"' :
case '\'' :
case '/' :
writeChar = currentChar ;
break ;
case 'u' :
_charPos = charPos ;
writeChar = await ParseUnicodeAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( StringUtils . IsLowSurrogate ( writeChar ) )
{
// low surrogate with no preceding high surrogate; this char is replaced
writeChar = UnicodeReplacementChar ;
}
else if ( StringUtils . IsHighSurrogate ( writeChar ) )
{
bool anotherHighSurrogate ;
// loop for handling situations where there are multiple consecutive high surrogates
do
{
anotherHighSurrogate = false ;
// potential start of a surrogate pair
if ( await EnsureCharsAsync ( 2 , true , cancellationToken ) . ConfigureAwait ( false ) & & _chars [ _charPos ] = = '\\' & & _chars [ _charPos + 1 ] = = 'u' )
{
char highSurrogate = writeChar ;
_charPos + = 2 ;
writeChar = await ParseUnicodeAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( StringUtils . IsLowSurrogate ( writeChar ) )
{
// a valid surrogate pair!
}
else if ( StringUtils . IsHighSurrogate ( writeChar ) )
{
// another high surrogate; replace current and start check over
highSurrogate = UnicodeReplacementChar ;
anotherHighSurrogate = true ;
}
else
{
// high surrogate not followed by low surrogate; original char is replaced
highSurrogate = UnicodeReplacementChar ;
}
EnsureBufferNotEmpty ( ) ;
WriteCharToBuffer ( highSurrogate , lastWritePosition , escapeStartPos ) ;
lastWritePosition = _charPos ;
}
else
{
// there are not enough remaining chars for the low surrogate or is not follow by unicode sequence
// replace high surrogate and continue on as usual
writeChar = UnicodeReplacementChar ;
}
} while ( anotherHighSurrogate ) ;
}
charPos = _charPos ;
break ;
default :
_charPos = charPos ;
throw JsonReaderException . Create ( this , "Bad JSON escape sequence: {0}." . FormatWith ( CultureInfo . InvariantCulture , @"\" + currentChar ) ) ;
}
EnsureBufferNotEmpty ( ) ;
WriteCharToBuffer ( writeChar , lastWritePosition , escapeStartPos ) ;
lastWritePosition = charPos ;
break ;
case StringUtils . CarriageReturn :
_charPos = charPos - 1 ;
await ProcessCarriageReturnAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
charPos = _charPos ;
break ;
case StringUtils . LineFeed :
_charPos = charPos - 1 ;
ProcessLineFeed ( ) ;
charPos = _charPos ;
break ;
case '"' :
case '\'' :
if ( _chars [ charPos - 1 ] = = quote )
{
FinishReadStringIntoBuffer ( charPos - 1 , initialPosition , lastWritePosition ) ;
return ;
}
break ;
}
}
}
private Task ProcessCarriageReturnAsync ( bool append , CancellationToken cancellationToken )
{
_charPos + + ;
Task < bool > task = EnsureCharsAsync ( 1 , append , cancellationToken ) ;
if ( task . IsCompletedSucessfully ( ) )
{
SetNewLine ( task . Result ) ;
return AsyncUtils . CompletedTask ;
}
return ProcessCarriageReturnAsync ( task ) ;
}
private async Task ProcessCarriageReturnAsync ( Task < bool > task )
{
SetNewLine ( await task . ConfigureAwait ( false ) ) ;
}
private async Task < char > ParseUnicodeAsync ( CancellationToken cancellationToken )
{
return ConvertUnicode ( await EnsureCharsAsync ( 4 , true , cancellationToken ) . ConfigureAwait ( false ) ) ;
}
private Task < bool > EnsureCharsAsync ( int relativePosition , bool append , CancellationToken cancellationToken )
{
if ( _charPos + relativePosition < _charsUsed )
{
return AsyncUtils . True ;
}
if ( _isEndOfFile )
{
return AsyncUtils . False ;
}
return ReadCharsAsync ( relativePosition , append , cancellationToken ) ;
}
private async Task < bool > ReadCharsAsync ( int relativePosition , bool append , CancellationToken cancellationToken )
{
int charsRequired = _charPos + relativePosition - _charsUsed + 1 ;
// it is possible that the TextReader doesn't return all data at once
// repeat read until the required text is returned or the reader is out of content
do
{
int charsRead = await ReadDataAsync ( append , charsRequired , cancellationToken ) . ConfigureAwait ( false ) ;
// no more content
if ( charsRead = = 0 )
{
return false ;
}
charsRequired - = charsRead ;
} while ( charsRequired > 0 ) ;
return true ;
}
private async Task < bool > ParseObjectAsync ( CancellationToken cancellationToken )
{
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( false , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
return false ;
}
}
else
{
_charPos + + ;
}
break ;
case '}' :
SetToken ( JsonToken . EndObject ) ;
_charPos + + ;
return true ;
case '/' :
await ParseCommentAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
return true ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
default :
if ( char . IsWhiteSpace ( currentChar ) )
{
// eat
_charPos + + ;
}
else
{
return await ParsePropertyAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
}
break ;
}
}
}
private async Task ParseCommentAsync ( bool setToken , CancellationToken cancellationToken )
{
// should have already parsed / character before reaching this method
_charPos + + ;
if ( ! await EnsureCharsAsync ( 1 , false , cancellationToken ) . ConfigureAwait ( false ) )
{
throw JsonReaderException . Create ( this , "Unexpected end while parsing comment." ) ;
}
bool singlelineComment ;
if ( _chars [ _charPos ] = = '*' )
{
singlelineComment = false ;
}
else if ( _chars [ _charPos ] = = '/' )
{
singlelineComment = true ;
}
else
{
throw JsonReaderException . Create ( this , "Error parsing comment. Expected: *, got {0}." . FormatWith ( CultureInfo . InvariantCulture , _chars [ _charPos ] ) ) ;
}
_charPos + + ;
int initialPosition = _charPos ;
while ( true )
{
switch ( _chars [ _charPos ] )
{
case '\0' :
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( true , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
if ( ! singlelineComment )
{
throw JsonReaderException . Create ( this , "Unexpected end while parsing comment." ) ;
}
EndComment ( setToken , initialPosition , _charPos ) ;
return ;
}
}
else
{
_charPos + + ;
}
break ;
case '*' :
_charPos + + ;
if ( ! singlelineComment )
{
if ( await EnsureCharsAsync ( 0 , true , cancellationToken ) . ConfigureAwait ( false ) )
{
if ( _chars [ _charPos ] = = '/' )
{
EndComment ( setToken , initialPosition , _charPos - 1 ) ;
_charPos + + ;
return ;
}
}
}
break ;
case StringUtils . CarriageReturn :
if ( singlelineComment )
{
EndComment ( setToken , initialPosition , _charPos ) ;
return ;
}
await ProcessCarriageReturnAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
if ( singlelineComment )
{
EndComment ( setToken , initialPosition , _charPos ) ;
return ;
}
ProcessLineFeed ( ) ;
break ;
default :
_charPos + + ;
break ;
}
}
}
private async Task EatWhitespaceAsync ( CancellationToken cancellationToken )
{
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( false , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
return ;
}
}
else
{
_charPos + + ;
}
break ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
default :
if ( currentChar = = ' ' | | char . IsWhiteSpace ( currentChar ) )
{
_charPos + + ;
}
else
{
return ;
}
break ;
}
}
}
private async Task ParseStringAsync ( char quote , ReadType readType , CancellationToken cancellationToken )
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
_charPos + + ;
ShiftBufferIfNeeded ( ) ;
await ReadStringIntoBufferAsync ( quote , cancellationToken ) . ConfigureAwait ( false ) ;
ParseReadString ( quote , readType ) ;
}
private async Task < bool > MatchValueAsync ( string value , CancellationToken cancellationToken )
{
return MatchValue ( await EnsureCharsAsync ( value . Length - 1 , true , cancellationToken ) . ConfigureAwait ( false ) , value ) ;
}
private async Task < bool > MatchValueWithTrailingSeparatorAsync ( string value , CancellationToken cancellationToken )
{
// will match value and then move to the next character, checking that it is a separator character
if ( ! await MatchValueAsync ( value , cancellationToken ) . ConfigureAwait ( false ) )
{
return false ;
}
if ( ! await EnsureCharsAsync ( 0 , false , cancellationToken ) . ConfigureAwait ( false ) )
{
return true ;
}
return IsSeparator ( _chars [ _charPos ] ) | | _chars [ _charPos ] = = '\0' ;
}
2021-03-30 10:54:25 +08:00
private async Task MatchAndSetAsync ( string value , JsonToken newToken , object tokenValue , CancellationToken cancellationToken )
2021-03-29 14:54:12 +08:00
{
if ( await MatchValueWithTrailingSeparatorAsync ( value , cancellationToken ) . ConfigureAwait ( false ) )
{
SetToken ( newToken , tokenValue ) ;
}
else
{
throw JsonReaderException . Create ( this , "Error parsing " + newToken . ToString ( ) . ToLowerInvariant ( ) + " value." ) ;
}
}
private Task ParseTrueAsync ( CancellationToken cancellationToken )
{
return MatchAndSetAsync ( JsonConvert . True , JsonToken . Boolean , true , cancellationToken ) ;
}
private Task ParseFalseAsync ( CancellationToken cancellationToken )
{
return MatchAndSetAsync ( JsonConvert . False , JsonToken . Boolean , false , cancellationToken ) ;
}
private Task ParseNullAsync ( CancellationToken cancellationToken )
{
return MatchAndSetAsync ( JsonConvert . Null , JsonToken . Null , null , cancellationToken ) ;
}
private async Task ParseConstructorAsync ( CancellationToken cancellationToken )
{
if ( await MatchValueWithTrailingSeparatorAsync ( "new" , cancellationToken ) . ConfigureAwait ( false ) )
{
await EatWhitespaceAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
int initialPosition = _charPos ;
int endPosition ;
while ( true )
{
char currentChar = _chars [ _charPos ] ;
if ( currentChar = = '\0' )
{
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( true , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
throw JsonReaderException . Create ( this , "Unexpected end while parsing constructor." ) ;
}
}
else
{
endPosition = _charPos ;
_charPos + + ;
break ;
}
}
else if ( char . IsLetterOrDigit ( currentChar ) )
{
_charPos + + ;
}
else if ( currentChar = = StringUtils . CarriageReturn )
{
endPosition = _charPos ;
await ProcessCarriageReturnAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
}
else if ( currentChar = = StringUtils . LineFeed )
{
endPosition = _charPos ;
ProcessLineFeed ( ) ;
break ;
}
else if ( char . IsWhiteSpace ( currentChar ) )
{
endPosition = _charPos ;
_charPos + + ;
break ;
}
else if ( currentChar = = '(' )
{
endPosition = _charPos ;
break ;
}
else
{
throw JsonReaderException . Create ( this , "Unexpected character while parsing constructor: {0}." . FormatWith ( CultureInfo . InvariantCulture , currentChar ) ) ;
}
}
_stringReference = new StringReference ( _chars , initialPosition , endPosition - initialPosition ) ;
string constructorName = _stringReference . ToString ( ) ;
await EatWhitespaceAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( _chars [ _charPos ] ! = '(' )
{
throw JsonReaderException . Create ( this , "Unexpected character while parsing constructor: {0}." . FormatWith ( CultureInfo . InvariantCulture , _chars [ _charPos ] ) ) ;
}
_charPos + + ;
ClearRecentString ( ) ;
SetToken ( JsonToken . StartConstructor , constructorName ) ;
}
else
{
throw JsonReaderException . Create ( this , "Unexpected content while parsing JSON." ) ;
}
}
private async Task < object > ParseNumberNaNAsync ( ReadType readType , CancellationToken cancellationToken )
{
return ParseNumberNaN ( readType , await MatchValueWithTrailingSeparatorAsync ( JsonConvert . NaN , cancellationToken ) . ConfigureAwait ( false ) ) ;
}
private async Task < object > ParseNumberPositiveInfinityAsync ( ReadType readType , CancellationToken cancellationToken )
{
return ParseNumberPositiveInfinity ( readType , await MatchValueWithTrailingSeparatorAsync ( JsonConvert . PositiveInfinity , cancellationToken ) . ConfigureAwait ( false ) ) ;
}
private async Task < object > ParseNumberNegativeInfinityAsync ( ReadType readType , CancellationToken cancellationToken )
{
return ParseNumberNegativeInfinity ( readType , await MatchValueWithTrailingSeparatorAsync ( JsonConvert . NegativeInfinity , cancellationToken ) . ConfigureAwait ( false ) ) ;
}
private async Task ParseNumberAsync ( ReadType readType , CancellationToken cancellationToken )
{
ShiftBufferIfNeeded ( ) ;
char firstChar = _chars [ _charPos ] ;
int initialPosition = _charPos ;
await ReadNumberIntoBufferAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
ParseReadNumber ( readType , firstChar , initialPosition ) ;
}
private Task ParseUndefinedAsync ( CancellationToken cancellationToken )
{
return MatchAndSetAsync ( JsonConvert . Undefined , JsonToken . Undefined , null , cancellationToken ) ;
}
private async Task < bool > ParsePropertyAsync ( CancellationToken cancellationToken )
{
char firstChar = _chars [ _charPos ] ;
char quoteChar ;
if ( firstChar = = '"' | | firstChar = = '\'' )
{
_charPos + + ;
quoteChar = firstChar ;
ShiftBufferIfNeeded ( ) ;
await ReadStringIntoBufferAsync ( quoteChar , cancellationToken ) . ConfigureAwait ( false ) ;
}
else if ( ValidIdentifierChar ( firstChar ) )
{
quoteChar = '\0' ;
ShiftBufferIfNeeded ( ) ;
await ParseUnquotedPropertyAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
}
else
{
throw JsonReaderException . Create ( this , "Invalid property identifier character: {0}." . FormatWith ( CultureInfo . InvariantCulture , _chars [ _charPos ] ) ) ;
}
string propertyName ;
if ( PropertyNameTable ! = null )
{
propertyName = PropertyNameTable . Get ( _stringReference . Chars , _stringReference . StartIndex , _stringReference . Length )
// no match in name table
? ? _stringReference . ToString ( ) ;
}
else
{
propertyName = _stringReference . ToString ( ) ;
}
await EatWhitespaceAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( _chars [ _charPos ] ! = ':' )
{
throw JsonReaderException . Create ( this , "Invalid character after parsing property name. Expected ':' but got: {0}." . FormatWith ( CultureInfo . InvariantCulture , _chars [ _charPos ] ) ) ;
}
_charPos + + ;
SetToken ( JsonToken . PropertyName , propertyName ) ;
_quoteChar = quoteChar ;
ClearRecentString ( ) ;
return true ;
}
private async Task ReadNumberIntoBufferAsync ( CancellationToken cancellationToken )
{
int charPos = _charPos ;
while ( true )
{
char currentChar = _chars [ charPos ] ;
if ( currentChar = = '\0' )
{
_charPos = charPos ;
if ( _charsUsed = = charPos )
{
if ( await ReadDataAsync ( true , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
return ;
}
}
else
{
return ;
}
}
else if ( ReadNumberCharIntoBuffer ( currentChar , charPos ) )
{
return ;
}
else
{
charPos + + ;
}
}
}
private async Task ParseUnquotedPropertyAsync ( CancellationToken cancellationToken )
{
int initialPosition = _charPos ;
// parse unquoted property name until whitespace or colon
while ( true )
{
char currentChar = _chars [ _charPos ] ;
if ( currentChar = = '\0' )
{
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( true , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
throw JsonReaderException . Create ( this , "Unexpected end while parsing unquoted property name." ) ;
}
continue ;
}
_stringReference = new StringReference ( _chars , initialPosition , _charPos - initialPosition ) ;
return ;
}
if ( ReadUnquotedPropertyReportIfDone ( currentChar , initialPosition ) )
{
return ;
}
}
}
private async Task < bool > ReadNullCharAsync ( CancellationToken cancellationToken )
{
if ( _charsUsed = = _charPos )
{
if ( await ReadDataAsync ( false , cancellationToken ) . ConfigureAwait ( false ) = = 0 )
{
_isEndOfFile = true ;
return true ;
}
}
else
{
_charPos + + ;
}
return false ;
}
private async Task HandleNullAsync ( CancellationToken cancellationToken )
{
if ( await EnsureCharsAsync ( 1 , true , cancellationToken ) . ConfigureAwait ( false ) )
{
if ( _chars [ _charPos + 1 ] = = 'u' )
{
await ParseNullAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return ;
}
_charPos + = 2 ;
throw CreateUnexpectedCharacterException ( _chars [ _charPos - 1 ] ) ;
}
_charPos = _charsUsed ;
throw CreateUnexpectedEndException ( ) ;
}
private async Task ReadFinishedAsync ( CancellationToken cancellationToken )
{
if ( await EnsureCharsAsync ( 0 , false , cancellationToken ) . ConfigureAwait ( false ) )
{
await EatWhitespaceAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( _isEndOfFile )
{
SetToken ( JsonToken . None ) ;
return ;
}
if ( _chars [ _charPos ] = = '/' )
{
await ParseCommentAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
}
else
{
throw JsonReaderException . Create ( this , "Additional text encountered after finished reading JSON content: {0}." . FormatWith ( CultureInfo . InvariantCulture , _chars [ _charPos ] ) ) ;
}
}
SetToken ( JsonToken . None ) ;
}
2021-03-30 10:54:25 +08:00
private async Task < object > ReadStringValueAsync ( ReadType readType , CancellationToken cancellationToken )
2021-03-29 14:54:12 +08:00
{
EnsureBuffer ( ) ;
switch ( _currentState )
{
case State . PostValue :
if ( await ParsePostValueAsync ( true , cancellationToken ) . ConfigureAwait ( false ) )
{
return null ;
}
goto case State . Start ;
case State . Start :
case State . Property :
case State . Array :
case State . ArrayStart :
case State . Constructor :
case State . ConstructorStart :
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( await ReadNullCharAsync ( cancellationToken ) . ConfigureAwait ( false ) )
{
SetToken ( JsonToken . None , null , false ) ;
return null ;
}
break ;
case '"' :
case '\'' :
await ParseStringAsync ( currentChar , readType , cancellationToken ) . ConfigureAwait ( false ) ;
return FinishReadQuotedStringValue ( readType ) ;
case '-' :
if ( await EnsureCharsAsync ( 1 , true , cancellationToken ) . ConfigureAwait ( false ) & & _chars [ _charPos + 1 ] = = 'I' )
{
return ParseNumberNegativeInfinity ( readType ) ;
}
else
{
await ParseNumberAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
return Value ;
}
case '.' :
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
if ( readType ! = ReadType . ReadAsString )
{
_charPos + + ;
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
await ParseNumberAsync ( ReadType . ReadAsString , cancellationToken ) . ConfigureAwait ( false ) ;
return Value ;
case 't' :
case 'f' :
if ( readType ! = ReadType . ReadAsString )
{
_charPos + + ;
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
string expected = currentChar = = 't' ? JsonConvert . True : JsonConvert . False ;
if ( ! await MatchValueWithTrailingSeparatorAsync ( expected , cancellationToken ) . ConfigureAwait ( false ) )
{
throw CreateUnexpectedCharacterException ( _chars [ _charPos ] ) ;
}
SetToken ( JsonToken . String , expected ) ;
return expected ;
case 'I' :
return await ParseNumberPositiveInfinityAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
case 'N' :
return await ParseNumberNaNAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
case 'n' :
await HandleNullAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
case '/' :
await ParseCommentAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case ',' :
ProcessValueComma ( ) ;
break ;
case ']' :
_charPos + + ;
if ( _currentState = = State . Array | | _currentState = = State . ArrayStart | | _currentState = = State . PostValue )
{
SetToken ( JsonToken . EndArray ) ;
return null ;
}
throw CreateUnexpectedCharacterException ( currentChar ) ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
default :
_charPos + + ;
if ( ! char . IsWhiteSpace ( currentChar ) )
{
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
// eat
break ;
}
}
case State . Finished :
await ReadFinishedAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
default :
throw JsonReaderException . Create ( this , "Unexpected state: {0}." . FormatWith ( CultureInfo . InvariantCulture , CurrentState ) ) ;
}
}
2021-03-30 10:54:25 +08:00
private async Task < object > ReadNumberValueAsync ( ReadType readType , CancellationToken cancellationToken )
2021-03-29 14:54:12 +08:00
{
EnsureBuffer ( ) ;
switch ( _currentState )
{
case State . PostValue :
if ( await ParsePostValueAsync ( true , cancellationToken ) . ConfigureAwait ( false ) )
{
return null ;
}
goto case State . Start ;
case State . Start :
case State . Property :
case State . Array :
case State . ArrayStart :
case State . Constructor :
case State . ConstructorStart :
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( await ReadNullCharAsync ( cancellationToken ) . ConfigureAwait ( false ) )
{
SetToken ( JsonToken . None , null , false ) ;
return null ;
}
break ;
case '"' :
case '\'' :
await ParseStringAsync ( currentChar , readType , cancellationToken ) . ConfigureAwait ( false ) ;
return FinishReadQuotedNumber ( readType ) ;
case 'n' :
await HandleNullAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
case 'N' :
return await ParseNumberNaNAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
case 'I' :
return await ParseNumberPositiveInfinityAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
case '-' :
if ( await EnsureCharsAsync ( 1 , true , cancellationToken ) . ConfigureAwait ( false ) & & _chars [ _charPos + 1 ] = = 'I' )
{
return await ParseNumberNegativeInfinityAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
}
else
{
await ParseNumberAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
return Value ;
}
case '.' :
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
await ParseNumberAsync ( readType , cancellationToken ) . ConfigureAwait ( false ) ;
return Value ;
case '/' :
await ParseCommentAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case ',' :
ProcessValueComma ( ) ;
break ;
case ']' :
_charPos + + ;
if ( _currentState = = State . Array | | _currentState = = State . ArrayStart | | _currentState = = State . PostValue )
{
SetToken ( JsonToken . EndArray ) ;
return null ;
}
throw CreateUnexpectedCharacterException ( currentChar ) ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
default :
_charPos + + ;
if ( ! char . IsWhiteSpace ( currentChar ) )
{
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
// eat
break ;
}
}
case State . Finished :
await ReadFinishedAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
default :
throw JsonReaderException . Create ( this , "Unexpected state: {0}." . FormatWith ( CultureInfo . InvariantCulture , CurrentState ) ) ;
}
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="Nullable{T}"/> of <see cref="bool"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="Nullable{T}"/> of <see cref="bool"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < bool? > ReadAsBooleanAsync ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsBooleanAsync ( cancellationToken ) : base . ReadAsBooleanAsync ( cancellationToken ) ;
}
internal async Task < bool? > DoReadAsBooleanAsync ( CancellationToken cancellationToken )
{
EnsureBuffer ( ) ;
switch ( _currentState )
{
case State . PostValue :
if ( await ParsePostValueAsync ( true , cancellationToken ) . ConfigureAwait ( false ) )
{
return null ;
}
goto case State . Start ;
case State . Start :
case State . Property :
case State . Array :
case State . ArrayStart :
case State . Constructor :
case State . ConstructorStart :
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( await ReadNullCharAsync ( cancellationToken ) . ConfigureAwait ( false ) )
{
SetToken ( JsonToken . None , null , false ) ;
return null ;
}
break ;
case '"' :
case '\'' :
await ParseStringAsync ( currentChar , ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
return ReadBooleanString ( _stringReference . ToString ( ) ) ;
case 'n' :
await HandleNullAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
case '-' :
case '.' :
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
await ParseNumberAsync ( ReadType . Read , cancellationToken ) . ConfigureAwait ( false ) ;
bool b ;
#if HAVE_BIG_INTEGER
if ( Value is BigInteger i )
{
b = i ! = 0 ;
}
else
# endif
{
b = Convert . ToBoolean ( Value , CultureInfo . InvariantCulture ) ;
}
SetToken ( JsonToken . Boolean , b , false ) ;
return b ;
case 't' :
case 'f' :
bool isTrue = currentChar = = 't' ;
if ( ! await MatchValueWithTrailingSeparatorAsync ( isTrue ? JsonConvert . True : JsonConvert . False , cancellationToken ) . ConfigureAwait ( false ) )
{
throw CreateUnexpectedCharacterException ( _chars [ _charPos ] ) ;
}
SetToken ( JsonToken . Boolean , isTrue ) ;
return isTrue ;
case '/' :
await ParseCommentAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case ',' :
ProcessValueComma ( ) ;
break ;
case ']' :
_charPos + + ;
if ( _currentState = = State . Array | | _currentState = = State . ArrayStart | | _currentState = = State . PostValue )
{
SetToken ( JsonToken . EndArray ) ;
return null ;
}
throw CreateUnexpectedCharacterException ( currentChar ) ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
default :
_charPos + + ;
if ( ! char . IsWhiteSpace ( currentChar ) )
{
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
// eat
break ;
}
}
case State . Finished :
await ReadFinishedAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
default :
throw JsonReaderException . Create ( this , "Unexpected state: {0}." . FormatWith ( CultureInfo . InvariantCulture , CurrentState ) ) ;
}
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="byte"/>[].
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="byte"/>[]. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
2021-03-30 10:54:25 +08:00
public override Task < byte [ ] > ReadAsBytesAsync ( CancellationToken cancellationToken = default )
2021-03-29 14:54:12 +08:00
{
return _safeAsync ? DoReadAsBytesAsync ( cancellationToken ) : base . ReadAsBytesAsync ( cancellationToken ) ;
}
2021-03-30 10:54:25 +08:00
internal async Task < byte [ ] > DoReadAsBytesAsync ( CancellationToken cancellationToken )
2021-03-29 14:54:12 +08:00
{
EnsureBuffer ( ) ;
bool isWrapped = false ;
switch ( _currentState )
{
case State . PostValue :
if ( await ParsePostValueAsync ( true , cancellationToken ) . ConfigureAwait ( false ) )
{
return null ;
}
goto case State . Start ;
case State . Start :
case State . Property :
case State . Array :
case State . ArrayStart :
case State . Constructor :
case State . ConstructorStart :
while ( true )
{
char currentChar = _chars [ _charPos ] ;
switch ( currentChar )
{
case '\0' :
if ( await ReadNullCharAsync ( cancellationToken ) . ConfigureAwait ( false ) )
{
SetToken ( JsonToken . None , null , false ) ;
return null ;
}
break ;
case '"' :
case '\'' :
await ParseStringAsync ( currentChar , ReadType . ReadAsBytes , cancellationToken ) . ConfigureAwait ( false ) ;
2021-03-30 10:54:25 +08:00
byte [ ] data = ( byte [ ] ) Value ;
2021-03-29 14:54:12 +08:00
if ( isWrapped )
{
await ReaderReadAndAssertAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( TokenType ! = JsonToken . EndObject )
{
throw JsonReaderException . Create ( this , "Error reading bytes. Unexpected token: {0}." . FormatWith ( CultureInfo . InvariantCulture , TokenType ) ) ;
}
SetToken ( JsonToken . Bytes , data , false ) ;
}
return data ;
case '{' :
_charPos + + ;
SetToken ( JsonToken . StartObject ) ;
await ReadIntoWrappedTypeObjectAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
isWrapped = true ;
break ;
case '[' :
_charPos + + ;
SetToken ( JsonToken . StartArray ) ;
return await ReadArrayIntoByteArrayAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
case 'n' :
await HandleNullAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
case '/' :
await ParseCommentAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case ',' :
ProcessValueComma ( ) ;
break ;
case ']' :
_charPos + + ;
if ( _currentState = = State . Array | | _currentState = = State . ArrayStart | | _currentState = = State . PostValue )
{
SetToken ( JsonToken . EndArray ) ;
return null ;
}
throw CreateUnexpectedCharacterException ( currentChar ) ;
case StringUtils . CarriageReturn :
await ProcessCarriageReturnAsync ( false , cancellationToken ) . ConfigureAwait ( false ) ;
break ;
case StringUtils . LineFeed :
ProcessLineFeed ( ) ;
break ;
case ' ' :
case StringUtils . Tab :
// eat
_charPos + + ;
break ;
default :
_charPos + + ;
if ( ! char . IsWhiteSpace ( currentChar ) )
{
throw CreateUnexpectedCharacterException ( currentChar ) ;
}
// eat
break ;
}
}
case State . Finished :
await ReadFinishedAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return null ;
default :
throw JsonReaderException . Create ( this , "Unexpected state: {0}." . FormatWith ( CultureInfo . InvariantCulture , CurrentState ) ) ;
}
}
private async Task ReadIntoWrappedTypeObjectAsync ( CancellationToken cancellationToken )
{
await ReaderReadAndAssertAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( Value ! = null & & Value . ToString ( ) = = JsonTypeReflector . TypePropertyName )
{
await ReaderReadAndAssertAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( Value ! = null & & Value . ToString ( ) . StartsWith ( "System.Byte[]" , StringComparison . Ordinal ) )
{
await ReaderReadAndAssertAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
if ( Value . ToString ( ) = = JsonTypeReflector . ValuePropertyName )
{
return ;
}
}
}
throw JsonReaderException . Create ( this , "Error reading bytes. Unexpected token: {0}." . FormatWith ( CultureInfo . InvariantCulture , JsonToken . StartObject ) ) ;
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="Nullable{T}"/> of <see cref="DateTime"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="Nullable{T}"/> of <see cref="DateTime"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < DateTime ? > ReadAsDateTimeAsync ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsDateTimeAsync ( cancellationToken ) : base . ReadAsDateTimeAsync ( cancellationToken ) ;
}
internal async Task < DateTime ? > DoReadAsDateTimeAsync ( CancellationToken cancellationToken )
{
return ( DateTime ? ) await ReadStringValueAsync ( ReadType . ReadAsDateTime , cancellationToken ) . ConfigureAwait ( false ) ;
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="Nullable{T}"/> of <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="Nullable{T}"/> of <see cref="DateTimeOffset"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < DateTimeOffset ? > ReadAsDateTimeOffsetAsync ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsDateTimeOffsetAsync ( cancellationToken ) : base . ReadAsDateTimeOffsetAsync ( cancellationToken ) ;
}
internal async Task < DateTimeOffset ? > DoReadAsDateTimeOffsetAsync ( CancellationToken cancellationToken )
{
return ( DateTimeOffset ? ) await ReadStringValueAsync ( ReadType . ReadAsDateTimeOffset , cancellationToken ) . ConfigureAwait ( false ) ;
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="Nullable{T}"/> of <see cref="decimal"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="Nullable{T}"/> of <see cref="decimal"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < decimal? > ReadAsDecimalAsync ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsDecimalAsync ( cancellationToken ) : base . ReadAsDecimalAsync ( cancellationToken ) ;
}
internal async Task < decimal? > DoReadAsDecimalAsync ( CancellationToken cancellationToken )
{
return ( decimal? ) await ReadNumberValueAsync ( ReadType . ReadAsDecimal , cancellationToken ) . ConfigureAwait ( false ) ;
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="Nullable{T}"/> of <see cref="double"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="Nullable{T}"/> of <see cref="double"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < double? > ReadAsDoubleAsync ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsDoubleAsync ( cancellationToken ) : base . ReadAsDoubleAsync ( cancellationToken ) ;
}
internal async Task < double? > DoReadAsDoubleAsync ( CancellationToken cancellationToken )
{
return ( double? ) await ReadNumberValueAsync ( ReadType . ReadAsDouble , cancellationToken ) . ConfigureAwait ( false ) ;
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="Nullable{T}"/> of <see cref="int"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="Nullable{T}"/> of <see cref="int"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
public override Task < int? > ReadAsInt32Async ( CancellationToken cancellationToken = default )
{
return _safeAsync ? DoReadAsInt32Async ( cancellationToken ) : base . ReadAsInt32Async ( cancellationToken ) ;
}
internal async Task < int? > DoReadAsInt32Async ( CancellationToken cancellationToken )
{
return ( int? ) await ReadNumberValueAsync ( ReadType . ReadAsInt32 , cancellationToken ) . ConfigureAwait ( false ) ;
}
/// <summary>
/// Asynchronously reads the next JSON token from the source as a <see cref="string"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous read. The <see cref="Task{TResult}.Result"/>
/// property returns the <see cref="string"/>. This result will be <c>null</c> at the end of an array.</returns>
/// <remarks>Derived classes must override this method to get asynchronous behaviour. Otherwise it will
/// execute synchronously, returning an already-completed task.</remarks>
2021-03-30 10:54:25 +08:00
public override Task < string > ReadAsStringAsync ( CancellationToken cancellationToken = default )
2021-03-29 14:54:12 +08:00
{
return _safeAsync ? DoReadAsStringAsync ( cancellationToken ) : base . ReadAsStringAsync ( cancellationToken ) ;
}
2021-03-30 10:54:25 +08:00
internal async Task < string > DoReadAsStringAsync ( CancellationToken cancellationToken )
2021-03-29 14:54:12 +08:00
{
2021-03-30 10:54:25 +08:00
return ( string ) await ReadStringValueAsync ( ReadType . ReadAsString , cancellationToken ) . ConfigureAwait ( false ) ;
2021-03-29 14:54:12 +08:00
}
}
}
# endif