<PackageReference Include="Newtonsoft.Json" Version="5.0.7" />

JsonTextReader

Represents a reader that provides fast, non-cached, forward-only access to JSON text data.
using Newtonsoft.Json.Utilities; using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Numerics; namespace Newtonsoft.Json { public class JsonTextReader : JsonReader, IJsonLineInfo { private const char UnicodeReplacementChar = '�'; private readonly TextReader _reader; private char[] _chars; private int _charsUsed; private int _charPos; private int _lineStartPos; private int _lineNumber; private bool _isEndOfFile; private StringBuffer _buffer; private StringReference _stringReference; public int LineNumber { get { if (base.CurrentState == State.Start && LinePosition == 0) return 0; return _lineNumber; } } public int LinePosition => _charPos - _lineStartPos; public JsonTextReader(TextReader reader) { if (reader == null) throw new ArgumentNullException("reader"); _reader = reader; _lineNumber = 1; _chars = new char[1025]; } private StringBuffer GetBuffer() { if (_buffer == null) _buffer = new StringBuffer(1025); else _buffer.Position = 0; return _buffer; } private void OnNewLine(int pos) { _lineNumber++; _lineStartPos = pos - 1; } private void ParseString(char quote) { _charPos++; ShiftBufferIfNeeded(); ReadStringIntoBuffer(quote); if (_readType == ReadType.ReadAsBytes) { byte[] value = (_stringReference.Length != 0) ? Convert.FromBase64CharArray(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length) : new byte[0]; SetToken(JsonToken.Bytes, value); } else if (_readType == ReadType.ReadAsString) { string value2 = _stringReference.ToString(); SetToken(JsonToken.String, value2); _quoteChar = quote; } else { string text = _stringReference.ToString(); if (_dateParseHandling != 0) { DateParseHandling dateParseHandling = (_readType == ReadType.ReadAsDateTime) ? DateParseHandling.DateTime : ((_readType != ReadType.ReadAsDateTimeOffset) ? _dateParseHandling : DateParseHandling.DateTimeOffset); if (DateTimeUtils.TryParseDateTime(text, dateParseHandling, base.DateTimeZoneHandling, out object dt)) { SetToken(JsonToken.Date, dt); return; } } SetToken(JsonToken.String, text); _quoteChar = quote; } } private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count) { Buffer.BlockCopy(src, srcOffset * 2, dst, dstOffset * 2, count * 2); } private void ShiftBufferIfNeeded() { int num = _chars.Length; if ((double)(num - _charPos) <= (double)num * 0.1) { int num2 = _charsUsed - _charPos; if (num2 > 0) BlockCopyChars(_chars, _charPos, _chars, 0, num2); _lineStartPos -= _charPos; _charPos = 0; _charsUsed = num2; _chars[_charsUsed] = ''; } } private int ReadData(bool append) { return ReadData(append, 0); } private int ReadData(bool append, int charsRequired) { if (_isEndOfFile) return 0; if (_charsUsed + charsRequired >= _chars.Length - 1) { if (append) { int num = Math.Max(_chars.Length * 2, _charsUsed + charsRequired + 1); char[] array = new char[num]; BlockCopyChars(_chars, 0, array, 0, _chars.Length); _chars = array; } else { int num2 = _charsUsed - _charPos; if (num2 + charsRequired + 1 >= _chars.Length) { char[] array2 = new char[num2 + charsRequired + 1]; if (num2 > 0) BlockCopyChars(_chars, _charPos, array2, 0, num2); _chars = array2; } else if (num2 > 0) { BlockCopyChars(_chars, _charPos, _chars, 0, num2); } _lineStartPos -= _charPos; _charPos = 0; _charsUsed = num2; } } int count = _chars.Length - _charsUsed - 1; int num3 = _reader.Read(_chars, _charsUsed, count); _charsUsed += num3; if (num3 == 0) _isEndOfFile = true; _chars[_charsUsed] = ''; return num3; } private bool EnsureChars(int relativePosition, bool append) { if (_charPos + relativePosition >= _charsUsed) return ReadChars(relativePosition, append); return true; } private bool ReadChars(int relativePosition, bool append) { if (_isEndOfFile) return false; int num = _charPos + relativePosition - _charsUsed + 1; int num2 = 0; do { int num3 = ReadData(append, num - num2); if (num3 == 0) break; num2 += num3; } while (num2 < num); if (num2 < num) return false; return true; } [DebuggerStepThrough] public override bool Read() { _readType = ReadType.Read; if (!ReadInternal()) { SetToken(JsonToken.None); return false; } return true; } public override byte[] ReadAsBytes() { return ReadAsBytesInternal(); } public override decimal? ReadAsDecimal() { return ReadAsDecimalInternal(); } public override int? ReadAsInt32() { return ReadAsInt32Internal(); } public override string ReadAsString() { return ReadAsStringInternal(); } public override DateTime? ReadAsDateTime() { return ReadAsDateTimeInternal(); } public override DateTimeOffset? ReadAsDateTimeOffset() { return ReadAsDateTimeOffsetInternal(); } internal override bool ReadInternal() { while (true) { switch (_currentState) { case State.Complete: case State.Closed: case State.Error: break; case State.Start: case State.Property: case State.ArrayStart: case State.Array: case State.ConstructorStart: case State.Constructor: return ParseValue(); case State.ObjectStart: case State.Object: return ParseObject(); case State.PostValue: if (ParsePostValue()) return true; break; case State.Finished: if (EnsureChars(0, false)) { EatWhitespace(false); if (_isEndOfFile) return false; if (_chars[_charPos] == '/') { ParseComment(); return true; } throw JsonReaderException.Create(this, "Additional text encountered after finished reading JSON content: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); } return false; default: throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, base.CurrentState)); } } } private void ReadStringIntoBuffer(char quote) { int num = _charPos; int charPos = _charPos; int num2 = _charPos; StringBuffer stringBuffer = null; while (true) { switch (_chars[num++]) { case '': if (_charsUsed == num - 1) { num--; if (ReadData(true) == 0) { _charPos = num; throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote)); } } break; case '\\': { _charPos = num; if (!EnsureChars(0, true)) { _charPos = num; throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote)); } int writeToPosition = num - 1; char c = _chars[num]; char c2; switch (c) { case 'b': num++; c2 = '\b'; break; case 't': num++; c2 = '\t'; break; case 'n': num++; c2 = '\n'; break; case 'f': num++; c2 = ' '; break; case 'r': num++; c2 = '\r'; break; case '\\': num++; c2 = '\\'; break; case '"': case '\'': case '/': c2 = c; num++; break; case 'u': num = (_charPos = num + 1); c2 = ParseUnicode(); if (StringUtils.IsLowSurrogate(c2)) c2 = '�'; else if (StringUtils.IsHighSurrogate(c2)) { bool flag; do { flag = false; if (EnsureChars(2, true) && _chars[_charPos] == '\\' && _chars[_charPos + 1] == 'u') { char writeChar = c2; _charPos += 2; c2 = ParseUnicode(); if (!StringUtils.IsLowSurrogate(c2)) { if (StringUtils.IsHighSurrogate(c2)) { writeChar = '�'; flag = true; } else writeChar = '�'; } if (stringBuffer == null) stringBuffer = GetBuffer(); WriteCharToBuffer(stringBuffer, writeChar, num2, writeToPosition); num2 = _charPos; } else c2 = '�'; } while (flag); } num = _charPos; break; default: num = (_charPos = num + 1); throw JsonReaderException.Create(this, "Bad JSON escape sequence: {0}.".FormatWith(CultureInfo.InvariantCulture, "\\" + c)); } if (stringBuffer == null) stringBuffer = GetBuffer(); WriteCharToBuffer(stringBuffer, c2, num2, writeToPosition); num2 = num; break; } case '\r': _charPos = num - 1; ProcessCarriageReturn(true); num = _charPos; break; case '\n': _charPos = num - 1; ProcessLineFeed(); num = _charPos; break; case '"': case '\'': if (_chars[num - 1] == quote) { num--; if (charPos == num2) _stringReference = new StringReference(_chars, charPos, num - charPos); else { if (stringBuffer == null) stringBuffer = GetBuffer(); if (num > num2) stringBuffer.Append(_chars, num2, num - num2); _stringReference = new StringReference(stringBuffer.GetInternalBuffer(), 0, stringBuffer.Position); } num = (_charPos = num + 1); return; } break; } } } private void WriteCharToBuffer(StringBuffer buffer, char writeChar, int lastWritePosition, int writeToPosition) { if (writeToPosition > lastWritePosition) buffer.Append(_chars, lastWritePosition, writeToPosition - lastWritePosition); buffer.Append(writeChar); } private char ParseUnicode() { if (!EnsureChars(4, true)) throw JsonReaderException.Create(this, "Unexpected end while parsing unicode character."); string s = new string(_chars, _charPos, 4); char c = Convert.ToChar(int.Parse(s, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); char result = c; _charPos += 4; return result; } private void ReadNumberIntoBuffer() { int num = _charPos; while (true) { switch (_chars[num++]) { case '+': case '-': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'X': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'x': break; case '': if (_charsUsed != num - 1) { _charPos = num - 1; return; } num = (_charPos = num - 1); if (ReadData(true) == 0) return; break; default: _charPos = num - 1; return; } } } private void ClearRecentString() { if (_buffer != null) _buffer.Position = 0; _stringReference = default(StringReference); } private bool ParsePostValue() { while (true) { char c = _chars[_charPos]; switch (c) { case '': if (_charsUsed == _charPos) { if (ReadData(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 '/': ParseComment(); return true; case ',': _charPos++; SetStateBasedOnCurrent(); return false; case '\t': case ' ': _charPos++; break; case '\r': ProcessCarriageReturn(false); break; case '\n': ProcessLineFeed(); break; default: if (!char.IsWhiteSpace(c)) throw JsonReaderException.Create(this, "After parsing a value an unexpected character was encountered: {0}.".FormatWith(CultureInfo.InvariantCulture, c)); _charPos++; break; } } } private bool ParseObject() { while (true) { char c = _chars[_charPos]; switch (c) { case '': if (_charsUsed == _charPos) { if (ReadData(false) == 0) return false; } else _charPos++; break; case '}': SetToken(JsonToken.EndObject); _charPos++; return true; case '/': ParseComment(); return true; case '\r': ProcessCarriageReturn(false); break; case '\n': ProcessLineFeed(); break; case '\t': case ' ': _charPos++; break; default: if (!char.IsWhiteSpace(c)) return ParseProperty(); _charPos++; break; } } } private bool ParseProperty() { char c = _chars[_charPos]; char c2; if (c == '"' || c == '\'') { _charPos++; c2 = c; ShiftBufferIfNeeded(); ReadStringIntoBuffer(c2); } else { if (!ValidIdentifierChar(c)) throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); c2 = ''; ShiftBufferIfNeeded(); ParseUnquotedProperty(); } string value = _stringReference.ToString(); EatWhitespace(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, value); _quoteChar = c2; ClearRecentString(); return true; } private bool ValidIdentifierChar(char value) { if (!char.IsLetterOrDigit(value) && value != '_') return value == '$'; return true; } private void ParseUnquotedProperty() { int charPos = _charPos; char c; while (true) { if (_chars[_charPos] == '') { if (_charsUsed != _charPos) { _stringReference = new StringReference(_chars, charPos, _charPos - charPos); return; } if (ReadData(true) == 0) throw JsonReaderException.Create(this, "Unexpected end while parsing unquoted property name."); } else { c = _chars[_charPos]; if (!ValidIdentifierChar(c)) break; _charPos++; } } if (char.IsWhiteSpace(c) || c == ':') { _stringReference = new StringReference(_chars, charPos, _charPos - charPos); return; } throw JsonReaderException.Create(this, "Invalid JavaScript property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, c)); } private bool ParseValue() { while (true) { char c = _chars[_charPos]; switch (c) { case '': if (_charsUsed == _charPos) { if (ReadData(false) == 0) return false; } else _charPos++; break; case '"': case '\'': ParseString(c); return true; case 't': ParseTrue(); return true; case 'f': ParseFalse(); return true; case 'n': if (!EnsureChars(1, true)) throw JsonReaderException.Create(this, "Unexpected end."); switch (_chars[_charPos + 1]) { case 'u': ParseNull(); break; case 'e': ParseConstructor(); break; default: throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); } return true; case 'N': ParseNumberNaN(); return true; case 'I': ParseNumberPositiveInfinity(); return true; case '-': if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I') ParseNumberNegativeInfinity(); else ParseNumber(); return true; case '/': ParseComment(); return true; case 'u': ParseUndefined(); return true; case '{': _charPos++; SetToken(JsonToken.StartObject); return true; case '[': _charPos++; SetToken(JsonToken.StartArray); return true; case ']': _charPos++; SetToken(JsonToken.EndArray); return true; case ',': SetToken(JsonToken.Undefined); return true; case ')': _charPos++; SetToken(JsonToken.EndConstructor); return true; case '\r': ProcessCarriageReturn(false); break; case '\n': ProcessLineFeed(); break; case '\t': case ' ': _charPos++; break; default: if (!char.IsWhiteSpace(c)) { if (char.IsNumber(c) || c == '-' || c == '.') { ParseNumber(); return true; } throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, c)); } _charPos++; break; } } } private void ProcessLineFeed() { _charPos++; OnNewLine(_charPos); } private void ProcessCarriageReturn(bool append) { _charPos++; if (EnsureChars(1, append) && _chars[_charPos] == '\n') _charPos++; OnNewLine(_charPos); } private bool EatWhitespace(bool oneOrMore) { bool flag = false; bool result = false; while (!flag) { char c = _chars[_charPos]; switch (c) { case '': if (_charsUsed == _charPos) { if (ReadData(false) == 0) flag = true; } else _charPos++; break; case '\r': ProcessCarriageReturn(false); break; case '\n': ProcessLineFeed(); break; default: if (c == ' ' || char.IsWhiteSpace(c)) { result = true; _charPos++; } else flag = true; break; } } if (oneOrMore) return result; return true; } private void ParseConstructor() { if (MatchValueWithTrailingSeperator("new")) { EatWhitespace(false); int charPos = _charPos; int charPos2; while (true) { char c = _chars[_charPos]; if (c == '') { if (_charsUsed != _charPos) { charPos2 = _charPos; _charPos++; break; } if (ReadData(true) == 0) throw JsonReaderException.Create(this, "Unexpected end while parsing constructor."); } else { if (!char.IsLetterOrDigit(c)) { switch (c) { case '\r': charPos2 = _charPos; ProcessCarriageReturn(true); break; case '\n': charPos2 = _charPos; ProcessLineFeed(); break; default: if (char.IsWhiteSpace(c)) { charPos2 = _charPos; _charPos++; } else { if (c != '(') throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, c)); charPos2 = _charPos; } break; } break; } _charPos++; } } _stringReference = new StringReference(_chars, charPos, charPos2 - charPos); string value = _stringReference.ToString(); EatWhitespace(false); if (_chars[_charPos] != '(') throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); _charPos++; ClearRecentString(); SetToken(JsonToken.StartConstructor, value); return; } throw JsonReaderException.Create(this, "Unexpected content while parsing JSON."); } private void ParseNumber() { ShiftBufferIfNeeded(); char c = _chars[_charPos]; int charPos = _charPos; ReadNumberIntoBuffer(); _stringReference = new StringReference(_chars, charPos, _charPos - charPos); bool flag = char.IsDigit(c) && _stringReference.Length == 1; bool flag2 = c == '0' && _stringReference.Length > 1 && _stringReference.Chars[_stringReference.StartIndex + 1] != '.' && _stringReference.Chars[_stringReference.StartIndex + 1] != 'e' && _stringReference.Chars[_stringReference.StartIndex + 1] != 'E'; object value; JsonToken newToken; if (_readType == ReadType.ReadAsInt32) { if (flag) value = c - 48; else if (flag2) { string text = _stringReference.ToString(); int num = text.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt32(text, 16) : Convert.ToInt32(text, 8); value = num; } else { value = ConvertUtils.Int32Parse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length); } newToken = JsonToken.Integer; } else if (_readType == ReadType.ReadAsDecimal) { if (flag) value = (decimal)c - 48; else if (flag2) { string text2 = _stringReference.ToString(); long value2 = text2.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(text2, 16) : Convert.ToInt64(text2, 8); value = Convert.ToDecimal(value2); } else { string s = _stringReference.ToString(); value = decimal.Parse(s, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent, CultureInfo.InvariantCulture); } newToken = JsonToken.Float; } else if (flag) { value = (long)c - 48; newToken = JsonToken.Integer; } else if (!flag2) { long value3; switch (ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value3)) { case ParseResult.Success: value = value3; newToken = JsonToken.Integer; break; case ParseResult.Invalid: { string text3 = _stringReference.ToString(); value = ((_floatParseHandling != FloatParseHandling.Decimal) ? ((object)Convert.ToDouble(text3, CultureInfo.InvariantCulture)) : ((object)decimal.Parse(text3, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent, CultureInfo.InvariantCulture))); newToken = JsonToken.Float; break; } case ParseResult.Overflow: { string value4 = _stringReference.ToString(); value = BigInteger.Parse(value4, CultureInfo.InvariantCulture); newToken = JsonToken.Integer; break; } default: throw JsonReaderException.Create(this, "Unknown error parsing integer."); } } else { string text4 = _stringReference.ToString(); value = (text4.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(text4, 16) : Convert.ToInt64(text4, 8)); newToken = JsonToken.Integer; } ClearRecentString(); SetToken(newToken, value); } private void ParseComment() { _charPos++; if (!EnsureChars(1, false) || _chars[_charPos] != '*') throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); _charPos++; int charPos = _charPos; bool flag = false; while (!flag) { switch (_chars[_charPos]) { case '': if (_charsUsed == _charPos) { if (ReadData(true) == 0) throw JsonReaderException.Create(this, "Unexpected end while parsing comment."); } else _charPos++; break; case '*': _charPos++; if (EnsureChars(0, true) && _chars[_charPos] == '/') { _stringReference = new StringReference(_chars, charPos, _charPos - charPos - 1); _charPos++; flag = true; } break; case '\r': ProcessCarriageReturn(true); break; case '\n': ProcessLineFeed(); break; default: _charPos++; break; } } SetToken(JsonToken.Comment, _stringReference.ToString()); ClearRecentString(); } private bool MatchValue(string value) { if (!EnsureChars(value.Length - 1, true)) return false; for (int i = 0; i < value.Length; i++) { if (_chars[_charPos + i] != value[i]) return false; } _charPos += value.Length; return true; } private bool MatchValueWithTrailingSeperator(string value) { if (!MatchValue(value)) return false; if (!EnsureChars(0, false)) return true; if (!IsSeperator(_chars[_charPos])) return _chars[_charPos] == ''; return true; } private bool IsSeperator(char c) { switch (c) { case ',': case ']': case '}': return true; case '/': if (!EnsureChars(1, false)) return false; return _chars[_charPos + 1] == '*'; case ')': if (base.CurrentState == State.Constructor || base.CurrentState == State.ConstructorStart) return true; break; case '\t': case '\n': case '\r': case ' ': return true; default: if (char.IsWhiteSpace(c)) return true; break; } return false; } private void ParseTrue() { if (MatchValueWithTrailingSeperator(JsonConvert.True)) { SetToken(JsonToken.Boolean, true); return; } throw JsonReaderException.Create(this, "Error parsing boolean value."); } private void ParseNull() { if (MatchValueWithTrailingSeperator(JsonConvert.Null)) { SetToken(JsonToken.Null); return; } throw JsonReaderException.Create(this, "Error parsing null value."); } private void ParseUndefined() { if (MatchValueWithTrailingSeperator(JsonConvert.Undefined)) { SetToken(JsonToken.Undefined); return; } throw JsonReaderException.Create(this, "Error parsing undefined value."); } private void ParseFalse() { if (MatchValueWithTrailingSeperator(JsonConvert.False)) { SetToken(JsonToken.Boolean, false); return; } throw JsonReaderException.Create(this, "Error parsing boolean value."); } private void ParseNumberNegativeInfinity() { if (MatchValueWithTrailingSeperator(JsonConvert.NegativeInfinity)) { if (_floatParseHandling == FloatParseHandling.Decimal) throw new JsonReaderException("Cannot read -Infinity as a decimal."); SetToken(JsonToken.Float, -Infinity); return; } throw JsonReaderException.Create(this, "Error parsing negative infinity value."); } private void ParseNumberPositiveInfinity() { if (MatchValueWithTrailingSeperator(JsonConvert.PositiveInfinity)) { if (_floatParseHandling == FloatParseHandling.Decimal) throw new JsonReaderException("Cannot read Infinity as a decimal."); SetToken(JsonToken.Float, Infinity); return; } throw JsonReaderException.Create(this, "Error parsing positive infinity value."); } private void ParseNumberNaN() { if (MatchValueWithTrailingSeperator(JsonConvert.NaN)) { if (_floatParseHandling == FloatParseHandling.Decimal) throw new JsonReaderException("Cannot read NaN as a decimal."); SetToken(JsonToken.Float, NaN); return; } throw JsonReaderException.Create(this, "Error parsing NaN value."); } public override void Close() { base.Close(); if (base.CloseInput && _reader != null) _reader.Close(); if (_buffer != null) _buffer.Clear(); } public bool HasLineInfo() { return true; } } }