|
|
#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 Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json { internal 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(CancellationToken)) { 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'; }
private async Task MatchAndSetAsync(string value, JsonToken newToken, object tokenValue, CancellationToken cancellationToken) { 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 (NameTable != null) { propertyName = NameTable.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); }
private async Task<object> ReadStringValueAsync(ReadType readType, 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, 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)); } }
private async Task<object> ReadNumberValueAsync(ReadType readType, 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, 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(CancellationToken)) { 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) { b = (BigInteger)Value != 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>
public override Task<byte[]> ReadAsBytesAsync(CancellationToken cancellationToken = default(CancellationToken)) { return _safeAsync ? DoReadAsBytesAsync(cancellationToken) : base.ReadAsBytesAsync(cancellationToken); }
internal async Task<byte[]> DoReadAsBytesAsync(CancellationToken cancellationToken) { 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); byte[] data = (byte[])Value; 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(CancellationToken)) { 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(CancellationToken)) { 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(CancellationToken)) { 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(CancellationToken)) { 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(CancellationToken)) { 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>
public override Task<string> ReadAsStringAsync(CancellationToken cancellationToken = default(CancellationToken)) { return _safeAsync ? DoReadAsStringAsync(cancellationToken) : base.ReadAsStringAsync(cancellationToken); }
internal async Task<string> DoReadAsStringAsync(CancellationToken cancellationToken) { return (string)await ReadStringValueAsync(ReadType.ReadAsString, cancellationToken).ConfigureAwait(false); } } }
#endif
|