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 }