2
// Copyright (c) 2007 James Newton-King
4
// Permission is hereby granted, free of charge, to any person
5
// obtaining a copy of this software and associated documentation
6
// files (the "Software"), to deal in the Software without
7
// restriction, including without limitation the rights to use,
8
// copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the
10
// Software is furnished to do so, subject to the following
13
// The above copyright notice and this permission notice shall be
14
// included in all copies or substantial portions of the Software.
16
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
// OTHER DEALINGS IN THE SOFTWARE.
27
using System.Collections.Generic;
28
using System.Diagnostics;
32
using System.Globalization;
33
using Newtonsoft.Json.Utilities;
35
namespace Newtonsoft.Json
37
internal enum ReadType
51
/// Represents a reader that provides fast, non-cached, forward-only access to JSON text data.
53
public class JsonTextReader : JsonReader, IJsonLineInfo
55
private const char UnicodeReplacementChar = '\uFFFD';
57
private readonly TextReader _reader;
58
private char[] _chars;
59
private int _charsUsed;
61
private int _lineStartPos;
62
private int _lineNumber;
63
private bool _isEndOfFile;
64
private StringBuffer _buffer;
65
private StringReference _stringReference;
68
/// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
70
/// <param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
71
public JsonTextReader(TextReader reader)
74
throw new ArgumentNullException("reader");
78
_chars = new char[4097];
81
internal void SetCharBuffer(char[] chars)
86
private StringBuffer GetBuffer()
90
_buffer = new StringBuffer(4096);
100
private void OnNewLine(int pos)
103
_lineStartPos = pos - 1;
106
private void ParseString(char quote)
110
ShiftBufferIfNeeded();
111
ReadStringIntoBuffer(quote);
113
if (_readType == ReadType.ReadAsBytes)
116
if (_stringReference.Length == 0)
122
data = Convert.FromBase64CharArray(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
125
SetToken(JsonToken.Bytes, data);
127
else if (_readType == ReadType.ReadAsString)
129
string text = _stringReference.ToString();
131
SetToken(JsonToken.String, text);
136
string text = _stringReference.ToString();
138
if (_dateParseHandling != DateParseHandling.None)
144
if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal))
146
ParseDateMicrosoft(text);
150
else if (char.IsDigit(text[0]) && text.Length >= 19 && text.Length <= 40)
152
if (ParseDateIso(text))
158
SetToken(JsonToken.String, text);
163
private bool ParseDateIso(string text)
165
const string isoDateFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK";
168
if (_readType == ReadType.ReadAsDateTimeOffset || (_readType == ReadType.Read && _dateParseHandling == DateParseHandling.DateTimeOffset))
170
DateTimeOffset dateTimeOffset;
171
if (DateTimeOffset.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset))
173
SetToken(JsonToken.Date, dateTimeOffset);
181
if (DateTime.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime))
183
dateTime = JsonConvert.EnsureDateTime(dateTime, DateTimeZoneHandling);
185
SetToken(JsonToken.Date, dateTime);
193
private void ParseDateMicrosoft(string text)
195
string value = text.Substring(6, text.Length - 8);
196
DateTimeKind kind = DateTimeKind.Utc;
198
int index = value.IndexOf('+', 1);
201
index = value.IndexOf('-', 1);
203
TimeSpan offset = TimeSpan.Zero;
207
kind = DateTimeKind.Local;
208
offset = ReadOffset(value.Substring(index));
209
value = value.Substring(0, index);
212
long javaScriptTicks = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
214
DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
217
if (_readType == ReadType.ReadAsDateTimeOffset || (_readType == ReadType.Read && _dateParseHandling == DateParseHandling.DateTimeOffset))
219
SetToken(JsonToken.Date, new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset));
228
case DateTimeKind.Unspecified:
229
dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
231
case DateTimeKind.Local:
232
dateTime = utcDateTime.ToLocalTime();
235
dateTime = utcDateTime;
239
dateTime = JsonConvert.EnsureDateTime(dateTime, DateTimeZoneHandling);
241
SetToken(JsonToken.Date, dateTime);
245
private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count)
247
const int charByteCount = 2;
249
Buffer.BlockCopy(src, srcOffset * charByteCount, dst, dstOffset * charByteCount, count * charByteCount);
252
private void ShiftBufferIfNeeded()
254
// once in the last 10% of the buffer shift the remainling content to the start to avoid
255
// unnessesarly increasing the buffer size when reading numbers/strings
256
int length = _chars.Length;
257
if (length - _charPos <= length * 0.1)
259
int count = _charsUsed - _charPos;
261
BlockCopyChars(_chars, _charPos, _chars, 0, count);
263
_lineStartPos -= _charPos;
266
_chars[_charsUsed] = '\0';
270
private int ReadData(bool append)
272
return ReadData(append, 0);
275
private int ReadData(bool append, int charsRequired)
280
// char buffer is full
281
if (_charsUsed + charsRequired >= _chars.Length - 1)
285
// copy to new array either double the size of the current or big enough to fit required content
286
int newArrayLength = Math.Max(_chars.Length * 2, _charsUsed + charsRequired + 1);
288
// increase the size of the buffer
289
char[] dst = new char[newArrayLength];
291
BlockCopyChars(_chars, 0, dst, 0, _chars.Length);
297
int remainingCharCount = _charsUsed - _charPos;
299
if (remainingCharCount + charsRequired + 1 >= _chars.Length)
301
// the remaining count plus the required is bigger than the current buffer size
302
char[] dst = new char[remainingCharCount + charsRequired + 1];
304
if (remainingCharCount > 0)
305
BlockCopyChars(_chars, _charPos, dst, 0, remainingCharCount);
311
// copy any remaining data to the beginning of the buffer if needed and reset positions
312
if (remainingCharCount > 0)
313
BlockCopyChars(_chars, _charPos, _chars, 0, remainingCharCount);
316
_lineStartPos -= _charPos;
318
_charsUsed = remainingCharCount;
322
int attemptCharReadCount = _chars.Length - _charsUsed - 1;
324
int charsRead = _reader.Read(_chars, _charsUsed, attemptCharReadCount);
326
_charsUsed += charsRead;
331
_chars[_charsUsed] = '\0';
335
private bool EnsureChars(int relativePosition, bool append)
337
if (_charPos + relativePosition >= _charsUsed)
338
return ReadChars(relativePosition, append);
343
private bool ReadChars(int relativePosition, bool append)
348
int charsRequired = _charPos + relativePosition - _charsUsed + 1;
350
int totalCharsRead = 0;
352
// it is possible that the TextReader doesn't return all data at once
353
// repeat read until the required text is returned or the reader is out of content
356
int charsRead = ReadData(append, charsRequired - totalCharsRead);
362
totalCharsRead += charsRead;
364
while (totalCharsRead < charsRequired);
366
if (totalCharsRead < charsRequired)
371
private static TimeSpan ReadOffset(string offsetText)
373
bool negative = (offsetText[0] == '-');
375
int hours = int.Parse(offsetText.Substring(1, 2), NumberStyles.Integer, CultureInfo.InvariantCulture);
377
if (offsetText.Length >= 5)
378
minutes = int.Parse(offsetText.Substring(3, 2), NumberStyles.Integer, CultureInfo.InvariantCulture);
380
TimeSpan offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes);
382
offset = offset.Negate();
388
/// Reads the next JSON token from the stream.
391
/// true if the next token was read successfully; false if there are no more tokens to read.
393
[DebuggerStepThrough]
394
public override bool Read()
396
_readType = ReadType.Read;
399
SetToken(JsonToken.None);
407
/// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
410
/// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
412
public override byte[] ReadAsBytes()
414
return ReadAsBytesInternal();
418
/// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
420
/// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns>
421
public override decimal? ReadAsDecimal()
423
return ReadAsDecimalInternal();
427
/// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>.
429
/// <returns>A <see cref="Nullable{Int32}"/>. This method will return <c>null</c> at the end of an array.</returns>
430
public override int? ReadAsInt32()
432
return ReadAsInt32Internal();
436
/// Reads the next JSON token from the stream as a <see cref="String"/>.
438
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
439
public override string ReadAsString()
441
return ReadAsStringInternal();
445
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
447
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
448
public override DateTime? ReadAsDateTime()
450
return ReadAsDateTimeInternal();
455
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
457
/// <returns>A <see cref="DateTimeOffset"/>. This method will return <c>null</c> at the end of an array.</returns>
458
public override DateTimeOffset? ReadAsDateTimeOffset()
460
return ReadAsDateTimeOffsetInternal();
464
internal override bool ReadInternal()
468
switch (_currentState)
473
case State.ArrayStart:
474
case State.Constructor:
475
case State.ConstructorStart:
480
case State.ObjectStart:
481
return ParseObject();
482
case State.PostValue:
483
// returns true if it hits
484
// end of object or array
485
if (ParsePostValue())
489
if (EnsureChars(0, false))
491
EatWhitespace(false);
496
if (_chars[_charPos] == '/')
503
throw JsonReaderException.Create(this, "Additional text encountered after finished reading JSON content: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
512
throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState));
517
private void ReadStringIntoBuffer(char quote)
519
int charPos = _charPos;
520
int initialPosition = _charPos;
521
int lastWritePosition = _charPos;
522
StringBuffer buffer = null;
526
switch (_chars[charPos++])
529
if (_charsUsed == charPos - 1)
533
if (ReadData(true) == 0)
536
throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote));
542
if (!EnsureChars(0, true))
545
throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote));
548
// start of escape sequence
549
int escapeStartPos = charPos - 1;
551
char currentChar = _chars[charPos];
584
writeChar = currentChar;
590
writeChar = ParseUnicode();
592
if (StringUtils.IsLowSurrogate(writeChar))
594
// low surrogate with no preceding high surrogate; this char is replaced
595
writeChar = UnicodeReplacementChar;
597
else if (StringUtils.IsHighSurrogate(writeChar))
599
bool anotherHighSurrogate;
601
// loop for handling situations where there are multiple consecutive high surrogates
604
anotherHighSurrogate = false;
606
// potential start of a surrogate pair
607
if (EnsureChars(2, true) && _chars[_charPos] == '\\' && _chars[_charPos + 1] == 'u')
609
char highSurrogate = writeChar;
612
writeChar = ParseUnicode();
614
if (StringUtils.IsLowSurrogate(writeChar))
616
// a valid surrogate pair!
618
else if (StringUtils.IsHighSurrogate(writeChar))
620
// another high surrogate; replace current and start check over
621
highSurrogate = UnicodeReplacementChar;
622
anotherHighSurrogate = true;
626
// high surrogate not followed by low surrogate; original char is replaced
627
highSurrogate = UnicodeReplacementChar;
631
buffer = GetBuffer();
633
WriteCharToBuffer(buffer, highSurrogate, lastWritePosition, escapeStartPos);
634
lastWritePosition = _charPos;
638
// there are not enough remaining chars for the low surrogate or is not follow by unicode sequence
639
// replace high surrogate and continue on as usual
640
writeChar = UnicodeReplacementChar;
642
} while (anotherHighSurrogate);
650
throw JsonReaderException.Create(this, "Bad JSON escape sequence: {0}.".FormatWith(CultureInfo.InvariantCulture, @"\" + currentChar));
654
buffer = GetBuffer();
656
WriteCharToBuffer(buffer, writeChar, lastWritePosition, escapeStartPos);
658
lastWritePosition = charPos;
660
case StringUtils.CarriageReturn:
661
_charPos = charPos - 1;
662
ProcessCarriageReturn(true);
665
case StringUtils.LineFeed:
666
_charPos = charPos - 1;
672
if (_chars[charPos - 1] == quote)
676
if (initialPosition == lastWritePosition)
678
_stringReference = new StringReference(_chars, initialPosition, charPos - initialPosition);
683
buffer = GetBuffer();
685
if (charPos > lastWritePosition)
686
buffer.Append(_chars, lastWritePosition, charPos - lastWritePosition);
688
_stringReference = new StringReference(buffer.GetInternalBuffer(), 0, buffer.Position);
700
private void WriteCharToBuffer(StringBuffer buffer, char writeChar, int lastWritePosition, int writeToPosition)
702
if (writeToPosition > lastWritePosition)
704
buffer.Append(_chars, lastWritePosition, writeToPosition - lastWritePosition);
707
buffer.Append(writeChar);
710
private char ParseUnicode()
713
if (EnsureChars(4, true))
715
string hexValues = new string(_chars, _charPos, 4);
716
char hexChar = Convert.ToChar(int.Parse(hexValues, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo));
723
throw JsonReaderException.Create(this, "Unexpected end while parsing unicode character.");
728
private void ReadNumberIntoBuffer()
730
int charPos = _charPos;
734
switch (_chars[charPos++])
737
if (_charsUsed == charPos - 1)
741
if (ReadData(true) == 0)
774
_charPos = charPos - 1;
780
private void ClearRecentString()
783
_buffer.Position = 0;
785
_stringReference = new StringReference();
788
private bool ParsePostValue()
792
char currentChar = _chars[_charPos];
797
if (_charsUsed == _charPos)
799
if (ReadData(false) == 0)
801
_currentState = State.Finished;
812
SetToken(JsonToken.EndObject);
816
SetToken(JsonToken.EndArray);
820
SetToken(JsonToken.EndConstructor);
829
SetStateBasedOnCurrent();
832
case StringUtils.Tab:
836
case StringUtils.CarriageReturn:
837
ProcessCarriageReturn(false);
839
case StringUtils.LineFeed:
843
if (char.IsWhiteSpace(currentChar))
850
throw JsonReaderException.Create(this, "After parsing a value an unexpected character was encountered: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
857
private bool ParseObject()
861
char currentChar = _chars[_charPos];
866
if (_charsUsed == _charPos)
868
if (ReadData(false) == 0)
877
SetToken(JsonToken.EndObject);
883
case StringUtils.CarriageReturn:
884
ProcessCarriageReturn(false);
886
case StringUtils.LineFeed:
890
case StringUtils.Tab:
895
if (char.IsWhiteSpace(currentChar))
902
return ParseProperty();
909
private bool ParseProperty()
911
char firstChar = _chars[_charPos];
914
if (firstChar == '"' || firstChar == '\'')
917
quoteChar = firstChar;
918
ShiftBufferIfNeeded();
919
ReadStringIntoBuffer(quoteChar);
921
else if (ValidIdentifierChar(firstChar))
924
ShiftBufferIfNeeded();
925
ParseUnquotedProperty();
929
throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
932
string propertyName = _stringReference.ToString();
934
EatWhitespace(false);
936
if (_chars[_charPos] != ':')
937
throw JsonReaderException.Create(this, "Invalid character after parsing property name. Expected ':' but got: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
941
SetToken(JsonToken.PropertyName, propertyName);
942
QuoteChar = quoteChar;
948
private bool ValidIdentifierChar(char value)
950
return (char.IsLetterOrDigit(value) || value == '_' || value == '$');
953
private void ParseUnquotedProperty()
955
int initialPosition = _charPos;
957
// parse unquoted property name until whitespace or colon
960
switch (_chars[_charPos])
963
if (_charsUsed == _charPos)
965
if (ReadData(true) == 0)
966
throw JsonReaderException.Create(this, "Unexpected end while parsing unquoted property name.");
971
_stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
974
char currentChar = _chars[_charPos];
976
if (ValidIdentifierChar(currentChar))
981
else if (char.IsWhiteSpace(currentChar) || currentChar == ':')
983
_stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
987
throw JsonReaderException.Create(this, "Invalid JavaScript property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
992
private bool ParseValue()
996
char currentChar = _chars[_charPos];
1001
if (_charsUsed == _charPos)
1003
if (ReadData(false) == 0)
1013
ParseString(currentChar);
1022
if (EnsureChars(1, true))
1024
char next = _chars[_charPos + 1];
1028
else if (next == 'e')
1031
throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
1035
throw JsonReaderException.Create(this, "Unexpected end.");
1042
ParseNumberPositiveInfinity();
1045
if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I')
1046
ParseNumberNegativeInfinity();
1058
SetToken(JsonToken.StartObject);
1062
SetToken(JsonToken.StartArray);
1066
SetToken(JsonToken.EndArray);
1069
// don't increment position, the next call to read will handle comma
1070
// this is done to handle multiple empty comma values
1071
SetToken(JsonToken.Undefined);
1075
SetToken(JsonToken.EndConstructor);
1077
case StringUtils.CarriageReturn:
1078
ProcessCarriageReturn(false);
1080
case StringUtils.LineFeed:
1084
case StringUtils.Tab:
1089
if (char.IsWhiteSpace(currentChar))
1095
else if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.')
1102
throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
1108
private void ProcessLineFeed()
1111
OnNewLine(_charPos);
1114
private void ProcessCarriageReturn(bool append)
1118
if (EnsureChars(1, append) && _chars[_charPos] == StringUtils.LineFeed)
1121
OnNewLine(_charPos);
1124
private bool EatWhitespace(bool oneOrMore)
1126
bool finished = false;
1127
bool ateWhitespace = false;
1130
char currentChar = _chars[_charPos];
1132
switch (currentChar)
1135
if (_charsUsed == _charPos)
1137
if (ReadData(false) == 0)
1145
case StringUtils.CarriageReturn:
1146
ProcessCarriageReturn(false);
1148
case StringUtils.LineFeed:
1152
if (currentChar == ' ' || char.IsWhiteSpace(currentChar))
1154
ateWhitespace = true;
1165
return (!oneOrMore || ateWhitespace);
1168
private void ParseConstructor()
1170
if (MatchValueWithTrailingSeperator("new"))
1172
EatWhitespace(false);
1174
int initialPosition = _charPos;
1179
char currentChar = _chars[_charPos];
1180
if (currentChar == '\0')
1182
if (_charsUsed == _charPos)
1184
if (ReadData(true) == 0)
1185
throw JsonReaderException.Create(this, "Unexpected end while parsing constructor.");
1189
endPosition = _charPos;
1194
else if (char.IsLetterOrDigit(currentChar))
1198
else if (currentChar == StringUtils.CarriageReturn)
1200
endPosition = _charPos;
1201
ProcessCarriageReturn(true);
1204
else if (currentChar == StringUtils.LineFeed)
1206
endPosition = _charPos;
1210
else if (char.IsWhiteSpace(currentChar))
1212
endPosition = _charPos;
1216
else if (currentChar == '(')
1218
endPosition = _charPos;
1223
throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
1227
_stringReference = new StringReference(_chars, initialPosition, endPosition - initialPosition);
1228
string constructorName = _stringReference.ToString();
1230
EatWhitespace(false);
1232
if (_chars[_charPos] != '(')
1233
throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
1237
ClearRecentString();
1239
SetToken(JsonToken.StartConstructor, constructorName);
1243
private void ParseNumber()
1245
ShiftBufferIfNeeded();
1247
char firstChar = _chars[_charPos];
1248
int initialPosition = _charPos;
1250
ReadNumberIntoBuffer();
1252
_stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
1255
JsonToken numberType;
1257
bool singleDigit = (char.IsDigit(firstChar) && _stringReference.Length == 1);
1258
bool nonBase10 = (firstChar == '0' && _stringReference.Length > 1
1259
&& _stringReference.Chars[_stringReference.StartIndex + 1] != '.'
1260
&& _stringReference.Chars[_stringReference.StartIndex + 1] != 'e'
1261
&& _stringReference.Chars[_stringReference.StartIndex + 1] != 'E');
1263
if (_readType == ReadType.ReadAsInt32)
1267
// digit char values start at 48
1268
numberValue = firstChar - 48;
1272
string number = _stringReference.ToString();
1274
// decimal.Parse doesn't support parsing hexadecimal values
1275
int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
1276
? Convert.ToInt32(number, 16)
1277
: Convert.ToInt32(number, 8);
1279
numberValue = integer;
1283
string number = _stringReference.ToString();
1285
numberValue = Convert.ToInt32(number, CultureInfo.InvariantCulture);
1288
numberType = JsonToken.Integer;
1290
else if (_readType == ReadType.ReadAsDecimal)
1294
// digit char values start at 48
1295
numberValue = (decimal)firstChar - 48;
1299
string number = _stringReference.ToString();
1301
// decimal.Parse doesn't support parsing hexadecimal values
1302
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
1303
? Convert.ToInt64(number, 16)
1304
: Convert.ToInt64(number, 8);
1306
numberValue = Convert.ToDecimal(integer);
1310
string number = _stringReference.ToString();
1312
numberValue = decimal.Parse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
1315
numberType = JsonToken.Float;
1321
// digit char values start at 48
1322
numberValue = (long)firstChar - 48;
1323
numberType = JsonToken.Integer;
1327
string number = _stringReference.ToString();
1329
numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
1330
? Convert.ToInt64(number, 16)
1331
: Convert.ToInt64(number, 8);
1332
numberType = JsonToken.Integer;
1336
string number = _stringReference.ToString();
1338
// it's faster to do 3 indexof with single characters than an indexofany
1339
if (number.IndexOf('.') != -1 || number.IndexOf('E') != -1 || number.IndexOf('e') != -1)
1341
numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture);
1342
numberType = JsonToken.Float;
1348
numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture);
1350
catch (OverflowException ex)
1352
throw JsonReaderException.Create((JsonReader)this, "JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, number), ex);
1355
numberType = JsonToken.Integer;
1360
ClearRecentString();
1362
SetToken(numberType, numberValue);
1365
private void ParseComment()
1367
// should have already parsed / character before reaching this method
1370
if (!EnsureChars(1, false) || _chars[_charPos] != '*')
1371
throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
1375
int initialPosition = _charPos;
1377
bool commentFinished = false;
1379
while (!commentFinished)
1381
switch (_chars[_charPos])
1384
if (_charsUsed == _charPos)
1386
if (ReadData(true) == 0)
1387
throw JsonReaderException.Create(this, "Unexpected end while parsing comment.");
1397
if (EnsureChars(0, true))
1399
if (_chars[_charPos] == '/')
1401
_stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition - 1);
1404
commentFinished = true;
1408
case StringUtils.CarriageReturn:
1409
ProcessCarriageReturn(true);
1411
case StringUtils.LineFeed:
1420
SetToken(JsonToken.Comment, _stringReference.ToString());
1422
ClearRecentString();
1425
private bool MatchValue(string value)
1427
if (!EnsureChars(value.Length - 1, true))
1430
for (int i = 0; i < value.Length; i++)
1432
if (_chars[_charPos + i] != value[i])
1438
_charPos += value.Length;
1443
private bool MatchValueWithTrailingSeperator(string value)
1445
// will match value and then move to the next character, checking that it is a seperator character
1446
bool match = MatchValue(value);
1451
if (!EnsureChars(0, false))
1454
return IsSeperator(_chars[_charPos]) || _chars[_charPos] == '\0';
1457
private bool IsSeperator(char c)
1466
// check next character to see if start of a comment
1467
if (!EnsureChars(1, false))
1470
return (_chars[_charPos + 1] == '*');
1472
if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart)
1476
case StringUtils.Tab:
1477
case StringUtils.LineFeed:
1478
case StringUtils.CarriageReturn:
1481
if (char.IsWhiteSpace(c))
1489
private void ParseTrue()
1491
// check characters equal 'true'
1492
// and that it is followed by either a seperator character
1494
if (MatchValueWithTrailingSeperator(JsonConvert.True))
1496
SetToken(JsonToken.Boolean, true);
1500
throw JsonReaderException.Create(this, "Error parsing boolean value.");
1504
private void ParseNull()
1506
if (MatchValueWithTrailingSeperator(JsonConvert.Null))
1508
SetToken(JsonToken.Null);
1512
throw JsonReaderException.Create(this, "Error parsing null value.");
1516
private void ParseUndefined()
1518
if (MatchValueWithTrailingSeperator(JsonConvert.Undefined))
1520
SetToken(JsonToken.Undefined);
1524
throw JsonReaderException.Create(this, "Error parsing undefined value.");
1528
private void ParseFalse()
1530
if (MatchValueWithTrailingSeperator(JsonConvert.False))
1532
SetToken(JsonToken.Boolean, false);
1536
throw JsonReaderException.Create(this, "Error parsing boolean value.");
1540
private void ParseNumberNegativeInfinity()
1542
if (MatchValueWithTrailingSeperator(JsonConvert.NegativeInfinity))
1544
SetToken(JsonToken.Float, double.NegativeInfinity);
1548
throw JsonReaderException.Create(this, "Error parsing negative infinity value.");
1552
private void ParseNumberPositiveInfinity()
1554
if (MatchValueWithTrailingSeperator(JsonConvert.PositiveInfinity))
1556
SetToken(JsonToken.Float, double.PositiveInfinity);
1560
throw JsonReaderException.Create(this, "Error parsing positive infinity value.");
1564
private void ParseNumberNaN()
1566
if (MatchValueWithTrailingSeperator(JsonConvert.NaN))
1568
SetToken(JsonToken.Float, double.NaN);
1572
throw JsonReaderException.Create(this, "Error parsing NaN value.");
1577
/// Changes the state to closed.
1579
public override void Close()
1583
if (CloseInput && _reader != null)
1584
#if !(NETFX_CORE || PORTABLE)
1590
if (_buffer != null)
1595
/// Gets a value indicating whether the class can return line information.
1598
/// <c>true</c> if LineNumber and LinePosition can be provided; otherwise, <c>false</c>.
1600
public bool HasLineInfo()
1606
/// Gets the current line number.
1609
/// The current line number or 0 if no line information is available (for example, HasLineInfo returns false).
1611
public int LineNumber
1615
if (CurrentState == State.Start && LinePosition == 0)
1623
/// Gets the current line position.
1626
/// The current line position or 0 if no line information is available (for example, HasLineInfo returns false).
1628
public int LinePosition
1630
get { return _charPos - _lineStartPos; }
b'\\ No newline at end of file'