1 #region License
2 // Copyright (c) 2007 James Newton-King
3 //
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
11 // conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
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.
24 #endregion
25 
26 using System;
27 using System.Collections.Generic;
28 using System.IO;
29 using System.Globalization;
30 #if !(NET20 || NET35 || PORTABLE40 || PORTABLE)
31 using System.Numerics;
32 #endif
33 using Newtonsoft.Json.Serialization;
34 using Newtonsoft.Json.Utilities;
35 #if NET20
36 using Newtonsoft.Json.Utilities.LinqBridge;
37 #else
38 using System.Linq;
39 #endif
40 
41 namespace Newtonsoft.Json
42 {
43     /// <summary>
44     /// Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data.
45     /// </summary>
46     public abstract class JsonReader : IDisposable
47     {
48         /// <summary>
49         /// Specifies the state of the reader.
50         /// </summary>
51         protected internal enum State
52         {
53             /// <summary>
54             /// The Read method has not been called.
55             /// </summary>
56             Start,
57 
58             /// <summary>
59             /// The end of the file has been reached successfully.
60             /// </summary>
61             Complete,
62 
63             /// <summary>
64             /// Reader is at a property.
65             /// </summary>
66             Property,
67 
68             /// <summary>
69             /// Reader is at the start of an object.
70             /// </summary>
71             ObjectStart,
72 
73             /// <summary>
74             /// Reader is in an object.
75             /// </summary>
76             Object,
77 
78             /// <summary>
79             /// Reader is at the start of an array.
80             /// </summary>
81             ArrayStart,
82 
83             /// <summary>
84             /// Reader is in an array.
85             /// </summary>
86             Array,
87 
88             /// <summary>
89             /// The Close method has been called.
90             /// </summary>
91             Closed,
92 
93             /// <summary>
94             /// Reader has just read a value.
95             /// </summary>
96             PostValue,
97 
98             /// <summary>
99             /// Reader is at the start of a constructor.
100             /// </summary>
101             ConstructorStart,
102 
103             /// <summary>
104             /// Reader in a constructor.
105             /// </summary>
106             Constructor,
107 
108             /// <summary>
109             /// An error occurred that prevents the read operation from continuing.
110             /// </summary>
111             Error,
112 
113             /// <summary>
114             /// The end of the file has been reached successfully.
115             /// </summary>
116             Finished
117         }
118 
119         // current Token data
120         private JsonToken _tokenType;
121         private object _value;
122         internal char _quoteChar;
123         internal State _currentState;
124         private JsonPosition _currentPosition;
125         private CultureInfo _culture;
126         private DateTimeZoneHandling _dateTimeZoneHandling;
127         private int? _maxDepth;
128         private bool _hasExceededMaxDepth;
129         internal DateParseHandling _dateParseHandling;
130         internal FloatParseHandling _floatParseHandling;
131         private string _dateFormatString;
132         private List<JsonPosition> _stack;
133 
134         /// <summary>
135         /// Gets the current reader state.
136         /// </summary>
137         /// <value>The current reader state.</value>
138         protected State CurrentState
139         {
140             get { return _currentState; }
141         }
142 
143         /// <summary>
144         /// Gets or sets a value indicating whether the underlying stream or
145         /// <see cref="TextReader"/> should be closed when the reader is closed.
146         /// </summary>
147         /// <value>
148         /// true to close the underlying stream or <see cref="TextReader"/> when
149         /// the reader is closed; otherwise false. The default is true.
150         /// </value>
151         public bool CloseInput { get; set; }
152 
153         /// <summary>
154         /// Gets or sets a value indicating whether multiple pieces of JSON content can
155         /// be read from a continuous stream without erroring.
156         /// </summary>
157         /// <value>
158         /// true to support reading multiple pieces of JSON content; otherwise false. The default is false.
159         /// </value>
160         public bool SupportMultipleContent { get; set; }
161 
162         /// <summary>
163         /// Gets the quotation mark character used to enclose the value of a string.
164         /// </summary>
165         public virtual char QuoteChar
166         {
167             get { return _quoteChar; }
168             protected internal set { _quoteChar = value; }
169         }
170 
171         /// <summary>
172         /// Get or set how <see cref="DateTime"/> time zones are handling when reading JSON.
173         /// </summary>
174         public DateTimeZoneHandling DateTimeZoneHandling
175         {
176             get { return _dateTimeZoneHandling; }
177             set
178             {
179                 if (value < DateTimeZoneHandling.Local || value > DateTimeZoneHandling.RoundtripKind)
180                 {
181                     throw new ArgumentOutOfRangeException(nameof(value));
182                 }
183 
184                 _dateTimeZoneHandling = value;
185             }
186         }
187 
188         /// <summary>
189         /// Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON.
190         /// </summary>
191         public DateParseHandling DateParseHandling
192         {
193             get { return _dateParseHandling; }
194             set
195             {
196                 if (value < DateParseHandling.None ||
197 #if !NET20
198                     value > DateParseHandling.DateTimeOffset
199 #else
200                     value > DateParseHandling.DateTime
201 #endif
202                     )
203                 {
204                     throw new ArgumentOutOfRangeException(nameof(value));
205                 }
206 
207                 _dateParseHandling = value;
208             }
209         }
210 
211         /// <summary>
212         /// Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text.
213         /// </summary>
214         public FloatParseHandling FloatParseHandling
215         {
216             get { return _floatParseHandling; }
217             set
218             {
219                 if (value < FloatParseHandling.Double || value > FloatParseHandling.Decimal)
220                 {
221                     throw new ArgumentOutOfRangeException(nameof(value));
222                 }
223 
224                 _floatParseHandling = value;
225             }
226         }
227 
228         /// <summary>
229         /// Get or set how custom date formatted strings are parsed when reading JSON.
230         /// </summary>
231         public string DateFormatString
232         {
233             get { return _dateFormatString; }
234             set { _dateFormatString = value; }
235         }
236 
237         /// <summary>
238         /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="JsonReaderException"/>.
239         /// </summary>
240         public int? MaxDepth
241         {
242             get { return _maxDepth; }
243             set
244             {
245                 if (value <= 0)
246                 {
247                     throw new ArgumentException("Value must be positive.", nameof(value));
248                 }
249 
250                 _maxDepth = value;
251             }
252         }
253 
254         /// <summary>
255         /// Gets the type of the current JSON token.
256         /// </summary>
257         public virtual JsonToken TokenType
258         {
259             get { return _tokenType; }
260         }
261 
262         /// <summary>
263         /// Gets the text value of the current JSON token.
264         /// </summary>
265         public virtual object Value
266         {
267             get { return _value; }
268         }
269 
270         /// <summary>
271         /// Gets The Common Language Runtime (CLR) type for the current JSON token.
272         /// </summary>
273         public virtual Type ValueType
274         {
275             get { return (_value != null) ? _value.GetType() : null; }
276         }
277 
278         /// <summary>
279         /// Gets the depth of the current token in the JSON document.
280         /// </summary>
281         /// <value>The depth of the current token in the JSON document.</value>
282         public virtual int Depth
283         {
284             get
285             {
286                 int depth = (_stack != null) ? _stack.Count : 0;
287                 if (JsonTokenUtils.IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None)
288                 {
289                     return depth;
290                 }
291                 else
292                 {
293                     return depth + 1;
294                 }
295             }
296         }
297 
298         /// <summary>
299         /// Gets the path of the current JSON token.
300         /// </summary>
301         public virtual string Path
302         {
303             get
304             {
305                 if (_currentPosition.Type == JsonContainerType.None)
306                 {
307                     return string.Empty;
308                 }
309 
310                 bool insideContainer = (_currentState != State.ArrayStart
311                                         && _currentState != State.ConstructorStart
312                                         && _currentState != State.ObjectStart);
313 
314                 JsonPosition? current = insideContainer ? (JsonPosition?)_currentPosition : null;
315 
316                 return JsonPosition.BuildPath(_stack, current);
317             }
318         }
319 
320         /// <summary>
321         /// Gets or sets the culture used when reading JSON. Defaults to <see cref="CultureInfo.InvariantCulture"/>.
322         /// </summary>
323         public CultureInfo Culture
324         {
325             get { return _culture ?? CultureInfo.InvariantCulture; }
326             set { _culture = value; }
327         }
328 
GetPosition(int depth)329         internal JsonPosition GetPosition(int depth)
330         {
331             if (_stack != null && depth < _stack.Count)
332             {
333                 return _stack[depth];
334             }
335 
336             return _currentPosition;
337         }
338 
339         /// <summary>
340         /// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
341         /// </summary>
JsonReader()342         protected JsonReader()
343         {
344             _currentState = State.Start;
345             _dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
346             _dateParseHandling = DateParseHandling.DateTime;
347             _floatParseHandling = FloatParseHandling.Double;
348 
349             CloseInput = true;
350         }
351 
Push(JsonContainerType value)352         private void Push(JsonContainerType value)
353         {
354             UpdateScopeWithFinishedValue();
355 
356             if (_currentPosition.Type == JsonContainerType.None)
357             {
358                 _currentPosition = new JsonPosition(value);
359             }
360             else
361             {
362                 if (_stack == null)
363                 {
364                     _stack = new List<JsonPosition>();
365                 }
366 
367                 _stack.Add(_currentPosition);
368                 _currentPosition = new JsonPosition(value);
369 
370                 // this is a little hacky because Depth increases when first property/value is written but only testing here is faster/simpler
371                 if (_maxDepth != null && Depth + 1 > _maxDepth && !_hasExceededMaxDepth)
372                 {
373                     _hasExceededMaxDepth = true;
374                     throw JsonReaderException.Create(this, "The reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth));
375                 }
376             }
377         }
378 
Pop()379         private JsonContainerType Pop()
380         {
381             JsonPosition oldPosition;
382             if (_stack != null && _stack.Count > 0)
383             {
384                 oldPosition = _currentPosition;
385                 _currentPosition = _stack[_stack.Count - 1];
386                 _stack.RemoveAt(_stack.Count - 1);
387             }
388             else
389             {
390                 oldPosition = _currentPosition;
391                 _currentPosition = new JsonPosition();
392             }
393 
394             if (_maxDepth != null && Depth <= _maxDepth)
395             {
396                 _hasExceededMaxDepth = false;
397             }
398 
399             return oldPosition.Type;
400         }
401 
Peek()402         private JsonContainerType Peek()
403         {
404             return _currentPosition.Type;
405         }
406 
407         /// <summary>
408         /// Reads the next JSON token from the stream.
409         /// </summary>
410         /// <returns><c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.</returns>
Read()411         public abstract bool Read();
412 
413         /// <summary>
414         /// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>.
415         /// </summary>
416         /// <returns>A <see cref="Nullable{Int32}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsInt32()417         public virtual int? ReadAsInt32()
418         {
419             JsonToken t = GetContentToken();
420 
421             switch (t)
422             {
423                 case JsonToken.None:
424                 case JsonToken.Null:
425                 case JsonToken.EndArray:
426                     return null;
427                 case JsonToken.Integer:
428                 case JsonToken.Float:
429                     if (!(Value is int))
430                     {
431                         SetToken(JsonToken.Integer, Convert.ToInt32(Value, CultureInfo.InvariantCulture), false);
432                     }
433 
434                     return (int)Value;
435                 case JsonToken.String:
436                     string s = (string)Value;
437                     return ReadInt32String(s);
438             }
439 
440             throw JsonReaderException.Create(this, "Error reading integer. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
441         }
442 
ReadInt32String(string s)443         internal int? ReadInt32String(string s)
444         {
445             if (string.IsNullOrEmpty(s))
446             {
447                 SetToken(JsonToken.Null, null, false);
448                 return null;
449             }
450 
451             int i;
452             if (int.TryParse(s, NumberStyles.Integer, Culture, out i))
453             {
454                 SetToken(JsonToken.Integer, i, false);
455                 return i;
456             }
457             else
458             {
459                 SetToken(JsonToken.String, s, false);
460                 throw JsonReaderException.Create(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
461             }
462         }
463 
464         /// <summary>
465         /// Reads the next JSON token from the stream as a <see cref="String"/>.
466         /// </summary>
467         /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsString()468         public virtual string ReadAsString()
469         {
470             JsonToken t = GetContentToken();
471 
472             switch (t)
473             {
474                 case JsonToken.None:
475                 case JsonToken.Null:
476                 case JsonToken.EndArray:
477                     return null;
478                 case JsonToken.String:
479                     return (string)Value;
480             }
481 
482             if (JsonTokenUtils.IsPrimitiveToken(t))
483             {
484                 if (Value != null)
485                 {
486                     string s;
487                     if (Value is IFormattable)
488                     {
489                         s = ((IFormattable)Value).ToString(null, Culture);
490                     }
491                     else if (Value is Uri)
492                     {
493                         s = ((Uri)Value).OriginalString;
494                     }
495                     else
496                     {
497                         s = Value.ToString();
498                     }
499 
500                     SetToken(JsonToken.String, s, false);
501                     return s;
502                 }
503             }
504 
505             throw JsonReaderException.Create(this, "Error reading string. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
506         }
507 
508         /// <summary>
509         /// Reads the next JSON token from the stream as a <see cref="Byte"/>[].
510         /// </summary>
511         /// <returns>A <see cref="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>
ReadAsBytes()512         public virtual byte[] ReadAsBytes()
513         {
514             JsonToken t = GetContentToken();
515 
516             if (t == JsonToken.None)
517             {
518                 return null;
519             }
520 
521             if (TokenType == JsonToken.StartObject)
522             {
523                 ReadIntoWrappedTypeObject();
524 
525                 byte[] data = ReadAsBytes();
526                 ReaderReadAndAssert();
527 
528                 if (TokenType != JsonToken.EndObject)
529                 {
530                     throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
531                 }
532 
533                 SetToken(JsonToken.Bytes, data, false);
534                 return data;
535             }
536 
537             switch (t)
538             {
539                 case JsonToken.String:
540                 {
541                     // attempt to convert possible base 64 or GUID string to bytes
542                     // GUID has to have format 00000000-0000-0000-0000-000000000000
543                     string s = (string)Value;
544 
545                     byte[] data;
546 
547                     Guid g;
548                     if (s.Length == 0)
549                     {
550                         data = new byte[0];
551                     }
552                     else if (ConvertUtils.TryConvertGuid(s, out g))
553                     {
554                         data = g.ToByteArray();
555                     }
556                     else
557                     {
558                         data = Convert.FromBase64String(s);
559                     }
560 
561                     SetToken(JsonToken.Bytes, data, false);
562                     return data;
563                 }
564                 case JsonToken.Null:
565                 case JsonToken.EndArray:
566                     return null;
567                 case JsonToken.Bytes:
568                     if (ValueType == typeof(Guid))
569                     {
570                         byte[] data = ((Guid)Value).ToByteArray();
571                         SetToken(JsonToken.Bytes, data, false);
572                         return data;
573                     }
574 
575                     return (byte[])Value;
576                 case JsonToken.StartArray:
577                     return ReadArrayIntoByteArray();
578             }
579 
580             throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
581         }
582 
ReadArrayIntoByteArray()583         internal byte[] ReadArrayIntoByteArray()
584         {
585             List<byte> buffer = new List<byte>();
586 
587             while (true)
588             {
589                 JsonToken t = GetContentToken();
590                 switch (t)
591                 {
592                     case JsonToken.None:
593                         throw JsonReaderException.Create(this, "Unexpected end when reading bytes.");
594                     case JsonToken.Integer:
595                         buffer.Add(Convert.ToByte(Value, CultureInfo.InvariantCulture));
596                         break;
597                     case JsonToken.EndArray:
598                         byte[] d = buffer.ToArray();
599                         SetToken(JsonToken.Bytes, d, false);
600                         return d;
601                     default:
602                         throw JsonReaderException.Create(this, "Unexpected token when reading bytes: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
603                 }
604             }
605         }
606 
607         /// <summary>
608         /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
609         /// </summary>
610         /// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDouble()611         public virtual double? ReadAsDouble()
612         {
613             JsonToken t = GetContentToken();
614 
615             switch (t)
616             {
617                 case JsonToken.None:
618                 case JsonToken.Null:
619                 case JsonToken.EndArray:
620                     return null;
621                 case JsonToken.Integer:
622                 case JsonToken.Float:
623                     if (!(Value is double))
624                     {
625                         double d;
626 #if !(NET20 || NET35 || PORTABLE40 || PORTABLE)
627                         if (Value is BigInteger)
628                         {
629                             d = (double)(BigInteger)Value;
630                         }
631                         else
632 #endif
633                         {
634                             d = Convert.ToDouble(Value, CultureInfo.InvariantCulture);
635                         }
636 
637                         SetToken(JsonToken.Float, d, false);
638                     }
639 
640                     return (double)Value;
641                 case JsonToken.String:
642                     return ReadDoubleString((string)Value);
643             }
644 
645             throw JsonReaderException.Create(this, "Error reading double. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
646         }
647 
ReadDoubleString(string s)648         internal double? ReadDoubleString(string s)
649         {
650             if (string.IsNullOrEmpty(s))
651             {
652                 SetToken(JsonToken.Null, null, false);
653                 return null;
654             }
655 
656             double d;
657             if (double.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, Culture, out d))
658             {
659                 SetToken(JsonToken.Float, d, false);
660                 return d;
661             }
662             else
663             {
664                 SetToken(JsonToken.String, s, false);
665                 throw JsonReaderException.Create(this, "Could not convert string to double: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
666             }
667         }
668 
669         /// <summary>
670         /// Reads the next JSON token from the stream as a <see cref="Nullable{Boolean}"/>.
671         /// </summary>
672         /// <returns>A <see cref="Nullable{Boolean}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsBoolean()673         public virtual bool? ReadAsBoolean()
674         {
675             JsonToken t = GetContentToken();
676 
677             switch (t)
678             {
679                 case JsonToken.None:
680                 case JsonToken.Null:
681                 case JsonToken.EndArray:
682                     return null;
683                 case JsonToken.Integer:
684                 case JsonToken.Float:
685                     bool b;
686 #if !(NET20 || NET35 || PORTABLE40 || PORTABLE)
687                     if (Value is BigInteger)
688                     {
689                         b = (BigInteger)Value != 0;
690                     }
691                     else
692 #endif
693                     {
694                         b = Convert.ToBoolean(Value, CultureInfo.InvariantCulture);
695                     }
696 
697                     SetToken(JsonToken.Boolean, b, false);
698 
699                     return b;
700                 case JsonToken.String:
701                     return ReadBooleanString((string)Value);
702                 case JsonToken.Boolean:
703                     return (bool)Value;
704             }
705 
706             throw JsonReaderException.Create(this, "Error reading boolean. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
707         }
708 
ReadBooleanString(string s)709         internal bool? ReadBooleanString(string s)
710         {
711             if (string.IsNullOrEmpty(s))
712             {
713                 SetToken(JsonToken.Null, null, false);
714                 return null;
715             }
716 
717             bool b;
718             if (bool.TryParse(s, out b))
719             {
720                 SetToken(JsonToken.Boolean, b, false);
721                 return b;
722             }
723             else
724             {
725                 SetToken(JsonToken.String, s, false);
726                 throw JsonReaderException.Create(this, "Could not convert string to boolean: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
727             }
728         }
729 
730         /// <summary>
731         /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
732         /// </summary>
733         /// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDecimal()734         public virtual decimal? ReadAsDecimal()
735         {
736             JsonToken t = GetContentToken();
737 
738             switch (t)
739             {
740                 case JsonToken.None:
741                 case JsonToken.Null:
742                 case JsonToken.EndArray:
743                     return null;
744                 case JsonToken.Integer:
745                 case JsonToken.Float:
746                     if (!(Value is decimal))
747                     {
748                         SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture), false);
749                     }
750 
751                     return (decimal)Value;
752                 case JsonToken.String:
753                     return ReadDecimalString((string)Value);
754             }
755 
756             throw JsonReaderException.Create(this, "Error reading decimal. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
757         }
758 
ReadDecimalString(string s)759         internal decimal? ReadDecimalString(string s)
760         {
761             if (string.IsNullOrEmpty(s))
762             {
763                 SetToken(JsonToken.Null, null, false);
764                 return null;
765             }
766 
767             decimal d;
768             if (decimal.TryParse(s, NumberStyles.Number, Culture, out d))
769             {
770                 SetToken(JsonToken.Float, d, false);
771                 return d;
772             }
773             else
774             {
775                 SetToken(JsonToken.String, s, false);
776                 throw JsonReaderException.Create(this, "Could not convert string to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
777             }
778         }
779 
780         /// <summary>
781         /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
782         /// </summary>
783         /// <returns>A <see cref="Nullable{DateTime}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDateTime()784         public virtual DateTime? ReadAsDateTime()
785         {
786             switch (GetContentToken())
787             {
788                 case JsonToken.None:
789                 case JsonToken.Null:
790                 case JsonToken.EndArray:
791                     return null;
792                 case JsonToken.Date:
793 #if !NET20
794                     if (Value is DateTimeOffset)
795                     {
796                         SetToken(JsonToken.Date, ((DateTimeOffset)Value).DateTime, false);
797                     }
798 #endif
799 
800                     return (DateTime)Value;
801                 case JsonToken.String:
802                     string s = (string)Value;
803                     return ReadDateTimeString(s);
804             }
805 
806             throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
807         }
808 
ReadDateTimeString(string s)809         internal DateTime? ReadDateTimeString(string s)
810         {
811             if (string.IsNullOrEmpty(s))
812             {
813                 SetToken(JsonToken.Null, null, false);
814                 return null;
815             }
816 
817             DateTime dt;
818             if (DateTimeUtils.TryParseDateTime(s, DateTimeZoneHandling, _dateFormatString, Culture, out dt))
819             {
820                 dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
821                 SetToken(JsonToken.Date, dt, false);
822                 return dt;
823             }
824 
825             if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
826             {
827                 dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
828                 SetToken(JsonToken.Date, dt, false);
829                 return dt;
830             }
831 
832             throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
833         }
834 
835 #if !NET20
836         /// <summary>
837         /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
838         /// </summary>
839         /// <returns>A <see cref="Nullable{DateTimeOffset}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDateTimeOffset()840         public virtual DateTimeOffset? ReadAsDateTimeOffset()
841         {
842             JsonToken t = GetContentToken();
843 
844             switch (t)
845             {
846                 case JsonToken.None:
847                 case JsonToken.Null:
848                 case JsonToken.EndArray:
849                     return null;
850                 case JsonToken.Date:
851                     if (Value is DateTime)
852                     {
853                         SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value), false);
854                     }
855 
856                     return (DateTimeOffset)Value;
857                 case JsonToken.String:
858                     string s = (string)Value;
859                     return ReadDateTimeOffsetString(s);
860                 default:
861                     throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t));
862             }
863         }
864 
ReadDateTimeOffsetString(string s)865         internal DateTimeOffset? ReadDateTimeOffsetString(string s)
866         {
867             if (string.IsNullOrEmpty(s))
868             {
869                 SetToken(JsonToken.Null, null, false);
870                 return null;
871             }
872 
873             DateTimeOffset dt;
874             if (DateTimeUtils.TryParseDateTimeOffset(s, _dateFormatString, Culture, out dt))
875             {
876                 SetToken(JsonToken.Date, dt, false);
877                 return dt;
878             }
879 
880             if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
881             {
882                 SetToken(JsonToken.Date, dt, false);
883                 return dt;
884             }
885 
886             SetToken(JsonToken.String, s, false);
887             throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
888         }
889 #endif
890 
ReaderReadAndAssert()891         internal void ReaderReadAndAssert()
892         {
893             if (!Read())
894             {
895                 throw CreateUnexpectedEndException();
896             }
897         }
898 
CreateUnexpectedEndException()899         internal JsonReaderException CreateUnexpectedEndException()
900         {
901             return JsonReaderException.Create(this, "Unexpected end when reading JSON.");
902         }
903 
ReadIntoWrappedTypeObject()904         internal void ReadIntoWrappedTypeObject()
905         {
906             ReaderReadAndAssert();
907             if (Value.ToString() == JsonTypeReflector.TypePropertyName)
908             {
909                 ReaderReadAndAssert();
910                 if (Value != null && Value.ToString().StartsWith("System.Byte[]", StringComparison.Ordinal))
911                 {
912                     ReaderReadAndAssert();
913                     if (Value.ToString() == JsonTypeReflector.ValuePropertyName)
914                     {
915                         return;
916                     }
917                 }
918             }
919 
920             throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, JsonToken.StartObject));
921         }
922 
923         /// <summary>
924         /// Skips the children of the current token.
925         /// </summary>
Skip()926         public void Skip()
927         {
928             if (TokenType == JsonToken.PropertyName)
929             {
930                 Read();
931             }
932 
933             if (JsonTokenUtils.IsStartToken(TokenType))
934             {
935                 int depth = Depth;
936 
937                 while (Read() && (depth < Depth))
938                 {
939                 }
940             }
941         }
942 
943         /// <summary>
944         /// Sets the current token.
945         /// </summary>
946         /// <param name="newToken">The new token.</param>
SetToken(JsonToken newToken)947         protected void SetToken(JsonToken newToken)
948         {
949             SetToken(newToken, null, true);
950         }
951 
952         /// <summary>
953         /// Sets the current token and value.
954         /// </summary>
955         /// <param name="newToken">The new token.</param>
956         /// <param name="value">The value.</param>
SetToken(JsonToken newToken, object value)957         protected void SetToken(JsonToken newToken, object value)
958         {
959             SetToken(newToken, value, true);
960         }
961 
SetToken(JsonToken newToken, object value, bool updateIndex)962         internal void SetToken(JsonToken newToken, object value, bool updateIndex)
963         {
964             _tokenType = newToken;
965             _value = value;
966 
967             switch (newToken)
968             {
969                 case JsonToken.StartObject:
970                     _currentState = State.ObjectStart;
971                     Push(JsonContainerType.Object);
972                     break;
973                 case JsonToken.StartArray:
974                     _currentState = State.ArrayStart;
975                     Push(JsonContainerType.Array);
976                     break;
977                 case JsonToken.StartConstructor:
978                     _currentState = State.ConstructorStart;
979                     Push(JsonContainerType.Constructor);
980                     break;
981                 case JsonToken.EndObject:
982                     ValidateEnd(JsonToken.EndObject);
983                     break;
984                 case JsonToken.EndArray:
985                     ValidateEnd(JsonToken.EndArray);
986                     break;
987                 case JsonToken.EndConstructor:
988                     ValidateEnd(JsonToken.EndConstructor);
989                     break;
990                 case JsonToken.PropertyName:
991                     _currentState = State.Property;
992 
993                     _currentPosition.PropertyName = (string)value;
994                     break;
995                 case JsonToken.Undefined:
996                 case JsonToken.Integer:
997                 case JsonToken.Float:
998                 case JsonToken.Boolean:
999                 case JsonToken.Null:
1000                 case JsonToken.Date:
1001                 case JsonToken.String:
1002                 case JsonToken.Raw:
1003                 case JsonToken.Bytes:
1004                     SetPostValueState(updateIndex);
1005                     break;
1006             }
1007         }
1008 
SetPostValueState(bool updateIndex)1009         internal void SetPostValueState(bool updateIndex)
1010         {
1011             if (Peek() != JsonContainerType.None)
1012             {
1013                 _currentState = State.PostValue;
1014             }
1015             else
1016             {
1017                 SetFinished();
1018             }
1019 
1020             if (updateIndex)
1021             {
1022                 UpdateScopeWithFinishedValue();
1023             }
1024         }
1025 
UpdateScopeWithFinishedValue()1026         private void UpdateScopeWithFinishedValue()
1027         {
1028             if (_currentPosition.HasIndex)
1029             {
1030                 _currentPosition.Position++;
1031             }
1032         }
1033 
ValidateEnd(JsonToken endToken)1034         private void ValidateEnd(JsonToken endToken)
1035         {
1036             JsonContainerType currentObject = Pop();
1037 
1038             if (GetTypeForCloseToken(endToken) != currentObject)
1039             {
1040                 throw JsonReaderException.Create(this, "JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject));
1041             }
1042 
1043             if (Peek() != JsonContainerType.None)
1044             {
1045                 _currentState = State.PostValue;
1046             }
1047             else
1048             {
1049                 SetFinished();
1050             }
1051         }
1052 
1053         /// <summary>
1054         /// Sets the state based on current token type.
1055         /// </summary>
SetStateBasedOnCurrent()1056         protected void SetStateBasedOnCurrent()
1057         {
1058             JsonContainerType currentObject = Peek();
1059 
1060             switch (currentObject)
1061             {
1062                 case JsonContainerType.Object:
1063                     _currentState = State.Object;
1064                     break;
1065                 case JsonContainerType.Array:
1066                     _currentState = State.Array;
1067                     break;
1068                 case JsonContainerType.Constructor:
1069                     _currentState = State.Constructor;
1070                     break;
1071                 case JsonContainerType.None:
1072                     SetFinished();
1073                     break;
1074                 default:
1075                     throw JsonReaderException.Create(this, "While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject));
1076             }
1077         }
1078 
SetFinished()1079         private void SetFinished()
1080         {
1081             if (SupportMultipleContent)
1082             {
1083                 _currentState = State.Start;
1084             }
1085             else
1086             {
1087                 _currentState = State.Finished;
1088             }
1089         }
1090 
GetTypeForCloseToken(JsonToken token)1091         private JsonContainerType GetTypeForCloseToken(JsonToken token)
1092         {
1093             switch (token)
1094             {
1095                 case JsonToken.EndObject:
1096                     return JsonContainerType.Object;
1097                 case JsonToken.EndArray:
1098                     return JsonContainerType.Array;
1099                 case JsonToken.EndConstructor:
1100                     return JsonContainerType.Constructor;
1101                 default:
1102                     throw JsonReaderException.Create(this, "Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token));
1103             }
1104         }
1105 
1106         /// <summary>
1107         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
1108         /// </summary>
IDisposable.Dispose()1109         void IDisposable.Dispose()
1110         {
1111             Dispose(true);
1112             GC.SuppressFinalize(this);
1113         }
1114 
1115         /// <summary>
1116         /// Releases unmanaged and - optionally - managed resources
1117         /// </summary>
1118         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
Dispose(bool disposing)1119         protected virtual void Dispose(bool disposing)
1120         {
1121             if (_currentState != State.Closed && disposing)
1122             {
1123                 Close();
1124             }
1125         }
1126 
1127         /// <summary>
1128         /// Changes the <see cref="State"/> to Closed.
1129         /// </summary>
Close()1130         public virtual void Close()
1131         {
1132             _currentState = State.Closed;
1133             _tokenType = JsonToken.None;
1134             _value = null;
1135         }
1136 
ReadAndAssert()1137         internal void ReadAndAssert()
1138         {
1139             if (!Read())
1140             {
1141                 throw JsonSerializationException.Create(this, "Unexpected end when reading JSON.");
1142             }
1143         }
1144 
ReadAndMoveToContent()1145         internal bool ReadAndMoveToContent()
1146         {
1147             return Read() && MoveToContent();
1148         }
1149 
MoveToContent()1150         internal bool MoveToContent()
1151         {
1152             JsonToken t = TokenType;
1153             while (t == JsonToken.None || t == JsonToken.Comment)
1154             {
1155                 if (!Read())
1156                 {
1157                     return false;
1158                 }
1159 
1160                 t = TokenType;
1161             }
1162 
1163             return true;
1164         }
1165 
GetContentToken()1166         private JsonToken GetContentToken()
1167         {
1168             JsonToken t;
1169             do
1170             {
1171                 if (!Read())
1172                 {
1173                     SetToken(JsonToken.None);
1174                     return JsonToken.None;
1175                 }
1176                 else
1177                 {
1178                     t = TokenType;
1179                 }
1180             } while (t == JsonToken.Comment);
1181 
1182             return t;
1183         }
1184     }
1185 }