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;
29
using System.Globalization;
30
using Newtonsoft.Json.Utilities;
32
using Newtonsoft.Json.Utilities.LinqBridge;
37
namespace Newtonsoft.Json
40
/// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
42
public abstract class JsonReader : IDisposable
45
/// Specifies the state of the reader.
47
protected internal enum State
50
/// The Read method has not been called.
54
/// The end of the file has been reached successfully.
58
/// Reader is at a property.
62
/// Reader is at the start of an object.
66
/// Reader is in an object.
70
/// Reader is at the start of an array.
74
/// Reader is in an array.
78
/// The Close method has been called.
82
/// Reader has just read a value.
86
/// Reader is at the start of a constructor.
90
/// Reader in a constructor.
94
/// An error occurred that prevents the read operation from continuing.
98
/// The end of the file has been reached successfully.
103
// current Token data
104
private JsonToken _tokenType;
105
private object _value;
106
private char _quoteChar;
107
internal State _currentState;
108
internal ReadType _readType;
109
private JsonPosition _currentPosition;
110
private CultureInfo _culture;
111
private DateTimeZoneHandling _dateTimeZoneHandling;
112
private int? _maxDepth;
113
private bool _hasExceededMaxDepth;
114
internal DateParseHandling _dateParseHandling;
115
private readonly List<JsonPosition> _stack;
118
/// Gets the current reader state.
120
/// <value>The current reader state.</value>
121
protected State CurrentState
123
get { return _currentState; }
127
/// Gets or sets a value indicating whether the underlying stream or
128
/// <see cref="TextReader"/> should be closed when the reader is closed.
131
/// true to close the underlying stream or <see cref="TextReader"/> when
132
/// the reader is closed; otherwise false. The default is true.
134
public bool CloseInput { get; set; }
137
/// Gets the quotation mark character used to enclose the value of a string.
139
public virtual char QuoteChar
141
get { return _quoteChar; }
142
protected internal set { _quoteChar = value; }
146
/// Get or set how <see cref="DateTime"/> time zones are handling when reading JSON.
148
public DateTimeZoneHandling DateTimeZoneHandling
150
get { return _dateTimeZoneHandling; }
151
set { _dateTimeZoneHandling = value; }
155
/// Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON.
157
public DateParseHandling DateParseHandling
159
get { return _dateParseHandling; }
160
set { _dateParseHandling = value; }
164
/// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="JsonReaderException"/>.
168
get { return _maxDepth; }
172
throw new ArgumentException("Value must be positive.", "value");
179
/// Gets the type of the current JSON token.
181
public virtual JsonToken TokenType
183
get { return _tokenType; }
187
/// Gets the text value of the current JSON token.
189
public virtual object Value
191
get { return _value; }
195
/// Gets The Common Language Runtime (CLR) type for the current JSON token.
197
public virtual Type ValueType
199
get { return (_value != null) ? _value.GetType() : null; }
203
/// Gets the depth of the current token in the JSON document.
205
/// <value>The depth of the current token in the JSON document.</value>
206
public virtual int Depth
210
int depth = _stack.Count;
211
if (IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None)
219
/// Gets the path of the current JSON token.
221
public virtual string Path
225
if (_currentPosition.Type == JsonContainerType.None)
228
bool insideContainer = (_currentState != State.ArrayStart
229
&& _currentState != State.ConstructorStart
230
&& _currentState != State.ObjectStart);
232
IEnumerable<JsonPosition> positions = (!insideContainer)
234
: _stack.Concat(new[] {_currentPosition});
236
return JsonPosition.BuildPath(positions);
241
/// Gets or sets the culture used when reading JSON. Defaults to <see cref="CultureInfo.InvariantCulture"/>.
243
public CultureInfo Culture
245
get { return _culture ?? CultureInfo.InvariantCulture; }
246
set { _culture = value; }
249
internal JsonPosition GetPosition(int depth)
251
if (depth < _stack.Count)
252
return _stack[depth];
254
return _currentPosition;
258
/// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
260
protected JsonReader()
262
_currentState = State.Start;
263
_stack = new List<JsonPosition>(4);
264
_dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
265
_dateParseHandling = DateParseHandling.DateTime;
270
private void Push(JsonContainerType value)
272
UpdateScopeWithFinishedValue();
274
if (_currentPosition.Type == JsonContainerType.None)
276
_currentPosition = new JsonPosition(value);
280
_stack.Add(_currentPosition);
281
_currentPosition = new JsonPosition(value);
283
// this is a little hacky because Depth increases when first property/value is written but only testing here is faster/simpler
284
if (_maxDepth != null && Depth + 1 > _maxDepth && !_hasExceededMaxDepth)
286
_hasExceededMaxDepth = true;
287
throw JsonReaderException.Create(this, "The reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth));
292
private JsonContainerType Pop()
294
JsonPosition oldPosition;
295
if (_stack.Count > 0)
297
oldPosition = _currentPosition;
298
_currentPosition = _stack[_stack.Count - 1];
299
_stack.RemoveAt(_stack.Count - 1);
303
oldPosition = _currentPosition;
304
_currentPosition = new JsonPosition();
307
if (_maxDepth != null && Depth <= _maxDepth)
308
_hasExceededMaxDepth = false;
310
return oldPosition.Type;
313
private JsonContainerType Peek()
315
return _currentPosition.Type;
319
/// Reads the next JSON token from the stream.
321
/// <returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
322
public abstract bool Read();
325
/// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>.
327
/// <returns>A <see cref="Nullable{Int32}"/>. This method will return <c>null</c> at the end of an array.</returns>
328
public abstract int? ReadAsInt32();
331
/// Reads the next JSON token from the stream as a <see cref="String"/>.
333
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
334
public abstract string ReadAsString();
337
/// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
339
/// <returns>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.</returns>
340
public abstract byte[] ReadAsBytes();
343
/// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
345
/// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns>
346
public abstract decimal? ReadAsDecimal();
349
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
351
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
352
public abstract DateTime? ReadAsDateTime();
356
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
358
/// <returns>A <see cref="Nullable{DateTimeOffset}"/>. This method will return <c>null</c> at the end of an array.</returns>
359
public abstract DateTimeOffset? ReadAsDateTimeOffset();
362
internal virtual bool ReadInternal()
364
throw new NotImplementedException();
368
internal DateTimeOffset? ReadAsDateTimeOffsetInternal()
370
_readType = ReadType.ReadAsDateTimeOffset;
376
SetToken(JsonToken.None);
379
} while (TokenType == JsonToken.Comment);
381
if (TokenType == JsonToken.Date)
383
if (Value is DateTime)
384
SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value));
386
return (DateTimeOffset)Value;
389
if (TokenType == JsonToken.Null)
393
if (TokenType == JsonToken.String)
395
string s = (string)Value;
396
if (string.IsNullOrEmpty(s))
398
SetToken(JsonToken.Null);
402
if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
404
SetToken(JsonToken.Date, dt);
409
throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
413
if (TokenType == JsonToken.EndArray)
416
throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
420
internal byte[] ReadAsBytesInternal()
422
_readType = ReadType.ReadAsBytes;
428
SetToken(JsonToken.None);
431
} while (TokenType == JsonToken.Comment);
433
if (IsWrappedInTypeObject())
435
byte[] data = ReadAsBytes();
437
SetToken(JsonToken.Bytes, data);
441
// attempt to convert possible base 64 string to bytes
442
if (TokenType == JsonToken.String)
444
string s = (string)Value;
445
byte[] data = (s.Length == 0) ? new byte[0] : Convert.FromBase64String(s);
446
SetToken(JsonToken.Bytes, data);
449
if (TokenType == JsonToken.Null)
452
if (TokenType == JsonToken.Bytes)
453
return (byte[])Value;
455
if (TokenType == JsonToken.StartArray)
457
List<byte> data = new List<byte>();
459
while (ReadInternal())
463
case JsonToken.Integer:
464
data.Add(Convert.ToByte(Value, CultureInfo.InvariantCulture));
466
case JsonToken.EndArray:
467
byte[] d = data.ToArray();
468
SetToken(JsonToken.Bytes, d);
470
case JsonToken.Comment:
474
throw JsonReaderException.Create(this, "Unexpected token when reading bytes: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
478
throw JsonReaderException.Create(this, "Unexpected end when reading bytes.");
481
if (TokenType == JsonToken.EndArray)
484
throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
487
internal decimal? ReadAsDecimalInternal()
489
_readType = ReadType.ReadAsDecimal;
495
SetToken(JsonToken.None);
498
} while (TokenType == JsonToken.Comment);
500
if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float)
502
if (!(Value is decimal))
503
SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture));
505
return (decimal)Value;
508
if (TokenType == JsonToken.Null)
512
if (TokenType == JsonToken.String)
514
string s = (string)Value;
515
if (string.IsNullOrEmpty(s))
517
SetToken(JsonToken.Null);
521
if (decimal.TryParse(s, NumberStyles.Number, Culture, out d))
523
SetToken(JsonToken.Float, d);
528
throw JsonReaderException.Create(this, "Could not convert string to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
532
if (TokenType == JsonToken.EndArray)
535
throw JsonReaderException.Create(this, "Error reading decimal. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
538
internal int? ReadAsInt32Internal()
540
_readType = ReadType.ReadAsInt32;
546
SetToken(JsonToken.None);
549
} while (TokenType == JsonToken.Comment);
551
if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float)
554
SetToken(JsonToken.Integer, Convert.ToInt32(Value, CultureInfo.InvariantCulture));
559
if (TokenType == JsonToken.Null)
563
if (TokenType == JsonToken.String)
565
string s = (string)Value;
566
if (string.IsNullOrEmpty(s))
568
SetToken(JsonToken.Null);
572
if (int.TryParse(s, NumberStyles.Integer, Culture, out i))
574
SetToken(JsonToken.Integer, i);
579
throw JsonReaderException.Create(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
583
if (TokenType == JsonToken.EndArray)
586
throw JsonReaderException.Create(this, "Error reading integer. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
589
internal string ReadAsStringInternal()
591
_readType = ReadType.ReadAsString;
597
SetToken(JsonToken.None);
600
} while (TokenType == JsonToken.Comment);
602
if (TokenType == JsonToken.String)
603
return (string)Value;
605
if (TokenType == JsonToken.Null)
608
if (IsPrimitiveToken(TokenType))
613
if (ConvertUtils.IsConvertible(Value))
614
s = ConvertUtils.ToConvertible(Value).ToString(Culture);
615
else if (Value is IFormattable)
616
s = ((IFormattable)Value).ToString(null, Culture);
618
s = Value.ToString();
620
SetToken(JsonToken.String, s);
625
if (TokenType == JsonToken.EndArray)
628
throw JsonReaderException.Create(this, "Error reading string. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
631
internal DateTime? ReadAsDateTimeInternal()
633
_readType = ReadType.ReadAsDateTime;
639
SetToken(JsonToken.None);
642
} while (TokenType == JsonToken.Comment);
644
if (TokenType == JsonToken.Date)
645
return (DateTime)Value;
647
if (TokenType == JsonToken.Null)
651
if (TokenType == JsonToken.String)
653
string s = (string)Value;
654
if (string.IsNullOrEmpty(s))
656
SetToken(JsonToken.Null);
660
if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
662
dt = JsonConvert.EnsureDateTime(dt, DateTimeZoneHandling);
663
SetToken(JsonToken.Date, dt);
668
throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
672
if (TokenType == JsonToken.EndArray)
675
throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
678
private bool IsWrappedInTypeObject()
680
_readType = ReadType.Read;
682
if (TokenType == JsonToken.StartObject)
685
throw JsonReaderException.Create(this, "Unexpected end when reading bytes.");
687
if (Value.ToString() == "$type")
690
if (Value != null && Value.ToString().StartsWith("System.Byte[]"))
693
if (Value.ToString() == "$value")
700
throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, JsonToken.StartObject));
707
/// Skips the children of the current token.
711
if (TokenType == JsonToken.PropertyName)
714
if (IsStartToken(TokenType))
718
while (Read() && (depth < Depth))
725
/// Sets the current token.
727
/// <param name="newToken">The new token.</param>
728
protected void SetToken(JsonToken newToken)
730
SetToken(newToken, null);
734
/// Sets the current token and value.
736
/// <param name="newToken">The new token.</param>
737
/// <param name="value">The value.</param>
738
protected void SetToken(JsonToken newToken, object value)
740
_tokenType = newToken;
745
case JsonToken.StartObject:
746
_currentState = State.ObjectStart;
747
Push(JsonContainerType.Object);
749
case JsonToken.StartArray:
750
_currentState = State.ArrayStart;
751
Push(JsonContainerType.Array);
753
case JsonToken.StartConstructor:
754
_currentState = State.ConstructorStart;
755
Push(JsonContainerType.Constructor);
757
case JsonToken.EndObject:
758
ValidateEnd(JsonToken.EndObject);
760
case JsonToken.EndArray:
761
ValidateEnd(JsonToken.EndArray);
763
case JsonToken.EndConstructor:
764
ValidateEnd(JsonToken.EndConstructor);
766
case JsonToken.PropertyName:
767
_currentState = State.Property;
769
_currentPosition.PropertyName = (string) value;
771
case JsonToken.Undefined:
772
case JsonToken.Integer:
773
case JsonToken.Float:
774
case JsonToken.Boolean:
777
case JsonToken.String:
779
case JsonToken.Bytes:
780
_currentState = (Peek() != JsonContainerType.None) ? State.PostValue : State.Finished;
782
UpdateScopeWithFinishedValue();
787
private void UpdateScopeWithFinishedValue()
789
if (_currentPosition.HasIndex)
790
_currentPosition.Position++;
793
private void ValidateEnd(JsonToken endToken)
795
JsonContainerType currentObject = Pop();
797
if (GetTypeForCloseToken(endToken) != currentObject)
798
throw JsonReaderException.Create(this, "JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject));
800
_currentState = (Peek() != JsonContainerType.None) ? State.PostValue : State.Finished;
804
/// Sets the state based on current token type.
806
protected void SetStateBasedOnCurrent()
808
JsonContainerType currentObject = Peek();
810
switch (currentObject)
812
case JsonContainerType.Object:
813
_currentState = State.Object;
815
case JsonContainerType.Array:
816
_currentState = State.Array;
818
case JsonContainerType.Constructor:
819
_currentState = State.Constructor;
821
case JsonContainerType.None:
822
_currentState = State.Finished;
825
throw JsonReaderException.Create(this, "While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject));
829
internal static bool IsPrimitiveToken(JsonToken token)
833
case JsonToken.Integer:
834
case JsonToken.Float:
835
case JsonToken.String:
836
case JsonToken.Boolean:
837
case JsonToken.Undefined:
840
case JsonToken.Bytes:
847
internal static bool IsStartToken(JsonToken token)
851
case JsonToken.StartObject:
852
case JsonToken.StartArray:
853
case JsonToken.StartConstructor:
860
private JsonContainerType GetTypeForCloseToken(JsonToken token)
864
case JsonToken.EndObject:
865
return JsonContainerType.Object;
866
case JsonToken.EndArray:
867
return JsonContainerType.Array;
868
case JsonToken.EndConstructor:
869
return JsonContainerType.Constructor;
871
throw JsonReaderException.Create(this, "Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token));
876
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
878
void IDisposable.Dispose()
884
/// Releases unmanaged and - optionally - managed resources
886
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
887
protected virtual void Dispose(bool disposing)
889
if (_currentState != State.Closed && disposing)
894
/// Changes the <see cref="State"/> to Closed.
896
public virtual void Close()
898
_currentState = State.Closed;
899
_tokenType = JsonToken.None;
b'\\ No newline at end of file'