JsonReader.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. #region License
  2. // Copyright 2006 James Newton-King
  3. // http://www.newtonsoft.com
  4. //
  5. // Copyright 2007 Konstantin Triger <[email protected]>
  6. //
  7. // This work is licensed under the Creative Commons Attribution 2.5 License
  8. // http://creativecommons.org/licenses/by/2.5/
  9. //
  10. // You are free:
  11. // * to copy, distribute, display, and perform the work
  12. // * to make derivative works
  13. // * to make commercial use of the work
  14. //
  15. // Under the following conditions:
  16. // * For any reuse or distribution, you must make clear to others the license terms of this work.
  17. // * Any of these conditions can be waived if you get permission from the copyright holder.
  18. #endregion
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Text;
  22. using System.IO;
  23. using System.Xml;
  24. using System.Globalization;
  25. namespace Newtonsoft.Json
  26. {
  27. /// <summary>
  28. /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
  29. /// </summary>
  30. sealed class JsonReader : IDisposable
  31. {
  32. private enum State
  33. {
  34. Start,
  35. Complete,
  36. Property,
  37. ObjectStart,
  38. Object,
  39. ArrayStart,
  40. Array,
  41. Closed,
  42. PostValue,
  43. Constructor,
  44. ConstructorEnd,
  45. Error,
  46. Finished
  47. }
  48. private TextReader _reader;
  49. private char _currentChar;
  50. // current Token data
  51. private JsonToken _token;
  52. private object _value;
  53. private Type _valueType;
  54. private char _quoteChar;
  55. private StringBuilder _buffer;
  56. //private StringBuilder _testBuffer;
  57. private State _currentState;
  58. private int _top;
  59. private List<JsonType> _stack;
  60. /// <summary>
  61. /// Gets the quotation mark character used to enclose the value of a string.
  62. /// </summary>
  63. public char QuoteChar
  64. {
  65. get { return _quoteChar; }
  66. }
  67. /// <summary>
  68. /// Gets the type of the current Json token.
  69. /// </summary>
  70. public JsonToken TokenType
  71. {
  72. get { return _token; }
  73. }
  74. /// <summary>
  75. /// Gets the text value of the current Json token.
  76. /// </summary>
  77. public object Value
  78. {
  79. get { return _value; }
  80. }
  81. /// <summary>
  82. /// Gets The Common Language Runtime (CLR) type for the current Json token.
  83. /// </summary>
  84. public Type ValueType
  85. {
  86. get { return _valueType; }
  87. }
  88. /// <summary>
  89. /// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
  90. /// </summary>
  91. /// <param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
  92. public JsonReader(TextReader reader)
  93. {
  94. if (reader == null)
  95. throw new ArgumentNullException("reader");
  96. _reader = reader;
  97. _buffer = new StringBuilder(4096);
  98. //_testBuffer = new StringBuilder();
  99. _currentState = State.Start;
  100. _stack = new List<JsonType>();
  101. _top = 0;
  102. Push(JsonType.None);
  103. }
  104. private void Push(JsonType value)
  105. {
  106. _stack.Add(value);
  107. _top++;
  108. }
  109. private JsonType Pop()
  110. {
  111. JsonType value = Peek();
  112. _stack.RemoveAt(_stack.Count - 1);
  113. _top--;
  114. return value;
  115. }
  116. private JsonType Peek()
  117. {
  118. return _stack[_top - 1];
  119. }
  120. private void ParseString(char quote)
  121. {
  122. bool stringTerminated = false;
  123. while (!stringTerminated && MoveNext())
  124. {
  125. switch (_currentChar)
  126. {
  127. //case 0:
  128. //case 0x0A:
  129. //case 0x0D:
  130. // throw new JsonReaderException("Unterminated string");
  131. case '\\':
  132. if (MoveNext())
  133. {
  134. switch (_currentChar)
  135. {
  136. case 'b':
  137. _buffer.Append('\b');
  138. break;
  139. case 't':
  140. _buffer.Append('\t');
  141. break;
  142. case 'n':
  143. _buffer.Append('\n');
  144. break;
  145. case 'f':
  146. _buffer.Append('\f');
  147. break;
  148. case 'r':
  149. _buffer.Append('\r');
  150. break;
  151. case 'u':
  152. //_buffer.Append((char) Integer.parseInt(next(4), 16));
  153. break;
  154. case 'x':
  155. //_buffer.Append((char) Integer.parseInt(next(2), 16));
  156. break;
  157. default:
  158. _buffer.Append(_currentChar);
  159. break;
  160. }
  161. }
  162. else
  163. {
  164. throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote);
  165. }
  166. break;
  167. case '"':
  168. case '\'':
  169. if (_currentChar == quote)
  170. stringTerminated = true;
  171. else
  172. goto default;
  173. break;
  174. default:
  175. _buffer.Append(_currentChar);
  176. break;
  177. }
  178. }
  179. if (!stringTerminated)
  180. throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote);
  181. ClearCurrentChar();
  182. _currentState = State.PostValue;
  183. _token = JsonToken.String;
  184. _value = _buffer.ToString();
  185. _buffer.Length = 0;
  186. _valueType = typeof(string);
  187. _quoteChar = quote;
  188. }
  189. private bool MoveNext()
  190. {
  191. int value = _reader.Read();
  192. if (value != -1)
  193. {
  194. _currentChar = (char) value;
  195. //_testBuffer.Append(_currentChar);
  196. return true;
  197. }
  198. else
  199. {
  200. return false;
  201. }
  202. }
  203. private bool HasNext()
  204. {
  205. return (_reader.Peek() != -1);
  206. }
  207. private char PeekNext()
  208. {
  209. return (char) _reader.Peek();
  210. }
  211. private void ClearCurrentChar()
  212. {
  213. _currentChar = '\0';
  214. }
  215. private bool MoveTo(char value)
  216. {
  217. while (MoveNext())
  218. {
  219. if (_currentChar == value)
  220. return true;
  221. }
  222. return false;
  223. }
  224. /// <summary>
  225. /// Reads the next Json token from the stream.
  226. /// </summary>
  227. /// <returns></returns>
  228. public bool Read()
  229. {
  230. while (true)
  231. {
  232. if (_currentChar == '\0')
  233. {
  234. if (!MoveNext())
  235. return false;
  236. }
  237. switch (_currentState)
  238. {
  239. case State.Start:
  240. case State.Property:
  241. case State.Array:
  242. case State.ArrayStart:
  243. return ParseValue();
  244. case State.Complete:
  245. break;
  246. case State.Object:
  247. case State.ObjectStart:
  248. return ParseObject();
  249. case State.PostValue:
  250. // returns true if it hits
  251. // end of object or array
  252. if (ParsePostValue())
  253. return true;
  254. break;
  255. case State.Closed:
  256. break;
  257. case State.Error:
  258. break;
  259. default:
  260. throw new JsonReaderException("Unexpected state: " + _currentState);
  261. }
  262. }
  263. }
  264. private bool ParsePostValue()
  265. {
  266. do
  267. {
  268. switch (_currentChar)
  269. {
  270. case '}':
  271. SetToken(JsonToken.EndObject);
  272. ClearCurrentChar();
  273. return true;
  274. case ']':
  275. SetToken(JsonToken.EndArray);
  276. ClearCurrentChar();
  277. return true;
  278. case '/':
  279. ParseComment();
  280. return true;
  281. case ',':
  282. // finished paring
  283. SetStateBasedOnCurrent();
  284. ClearCurrentChar();
  285. return false;
  286. default:
  287. if (char.IsWhiteSpace(_currentChar))
  288. {
  289. // eat
  290. }
  291. else
  292. {
  293. throw new JsonReaderException("After parsing a value an unexpected character was encoutered: " + _currentChar);
  294. }
  295. break;
  296. }
  297. } while (MoveNext());
  298. return false;
  299. }
  300. private bool ParseObject()
  301. {
  302. do
  303. {
  304. switch (_currentChar)
  305. {
  306. case '}':
  307. SetToken(JsonToken.EndObject);
  308. return true;
  309. case '/':
  310. ParseComment();
  311. return true;
  312. case ',':
  313. SetToken(JsonToken.Undefined);
  314. return true;
  315. default:
  316. if (char.IsWhiteSpace(_currentChar))
  317. {
  318. // eat
  319. }
  320. else
  321. {
  322. return ParseProperty();
  323. }
  324. break;
  325. }
  326. } while (MoveNext());
  327. return false;
  328. }
  329. private bool ParseProperty()
  330. {
  331. if (ValidIdentifierChar(_currentChar))
  332. {
  333. ParseUnquotedProperty();
  334. }
  335. else if (_currentChar == '"' || _currentChar == '\'')
  336. {
  337. ParseQuotedProperty(_currentChar);
  338. }
  339. else
  340. {
  341. throw new JsonReaderException("Invalid property identifier character: " + _currentChar);
  342. }
  343. // finished property. move to colon
  344. if (_currentChar != ':')
  345. {
  346. MoveTo(':');
  347. }
  348. SetToken(JsonToken.PropertyName, _buffer.ToString());
  349. _buffer.Length = 0;
  350. return true;
  351. }
  352. private void ParseQuotedProperty(char quoteChar)
  353. {
  354. // parse property name until quoted char is hit
  355. while (MoveNext())
  356. {
  357. if (_currentChar == quoteChar)
  358. {
  359. return;
  360. }
  361. else
  362. {
  363. _buffer.Append(_currentChar);
  364. }
  365. }
  366. throw new JsonReaderException("Unclosed quoted property. Expected: " + quoteChar);
  367. }
  368. private bool ValidIdentifierChar(char value)
  369. {
  370. return (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '$');
  371. }
  372. private void ParseUnquotedProperty()
  373. {
  374. // parse unquoted property name until whitespace or colon
  375. _buffer.Append(_currentChar);
  376. while (MoveNext())
  377. {
  378. if (char.IsWhiteSpace(_currentChar) || _currentChar == ':')
  379. {
  380. break;
  381. }
  382. else if (ValidIdentifierChar(_currentChar))
  383. {
  384. _buffer.Append(_currentChar);
  385. }
  386. else
  387. {
  388. throw new JsonReaderException("Invalid JavaScript property identifier character: " + _currentChar);
  389. }
  390. }
  391. }
  392. private void SetToken(JsonToken newToken)
  393. {
  394. SetToken(newToken, null);
  395. }
  396. private void SetToken(JsonToken newToken, object value)
  397. {
  398. _token = newToken;
  399. switch (newToken)
  400. {
  401. case JsonToken.StartObject:
  402. _currentState = State.ObjectStart;
  403. Push(JsonType.Object);
  404. ClearCurrentChar();
  405. break;
  406. case JsonToken.StartArray:
  407. _currentState = State.ArrayStart;
  408. Push(JsonType.Array);
  409. ClearCurrentChar();
  410. break;
  411. case JsonToken.EndObject:
  412. ValidateEnd(JsonToken.EndObject);
  413. ClearCurrentChar();
  414. _currentState = State.PostValue;
  415. break;
  416. case JsonToken.EndArray:
  417. ValidateEnd(JsonToken.EndArray);
  418. ClearCurrentChar();
  419. _currentState = State.PostValue;
  420. break;
  421. case JsonToken.PropertyName:
  422. _currentState = State.Property;
  423. ClearCurrentChar();
  424. break;
  425. case JsonToken.Undefined:
  426. case JsonToken.Integer:
  427. case JsonToken.Float:
  428. case JsonToken.Boolean:
  429. case JsonToken.Null:
  430. case JsonToken.Constructor:
  431. case JsonToken.Date:
  432. _currentState = State.PostValue;
  433. break;
  434. }
  435. if (value != null)
  436. {
  437. _value = value;
  438. _valueType = value.GetType();
  439. }
  440. else
  441. {
  442. _value = null;
  443. _valueType = null;
  444. }
  445. }
  446. private bool ParseValue()
  447. {
  448. do
  449. {
  450. switch (_currentChar)
  451. {
  452. case '"':
  453. case '\'':
  454. ParseString(_currentChar);
  455. return true;
  456. case 't':
  457. ParseTrue();
  458. return true;
  459. case 'f':
  460. ParseFalse();
  461. return true;
  462. case 'n':
  463. if (HasNext())
  464. {
  465. char next = PeekNext();
  466. if (next == 'u')
  467. ParseNull();
  468. else if (next == 'e')
  469. ParseConstructor();
  470. else
  471. throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar);
  472. }
  473. else
  474. {
  475. throw new JsonReaderException("Unexpected end");
  476. }
  477. return true;
  478. case '/':
  479. ParseComment();
  480. return true;
  481. case 'u':
  482. ParseUndefined();
  483. return true;
  484. case '{':
  485. SetToken(JsonToken.StartObject);
  486. return true;
  487. case '[':
  488. SetToken(JsonToken.StartArray);
  489. return true;
  490. case '}':
  491. SetToken(JsonToken.EndObject);
  492. return true;
  493. case ']':
  494. SetToken(JsonToken.EndArray);
  495. return true;
  496. case ',':
  497. SetToken(JsonToken.Undefined);
  498. //ClearCurrentChar();
  499. return true;
  500. case ')':
  501. if (_currentState == State.Constructor)
  502. {
  503. _currentState = State.ConstructorEnd;
  504. return false;
  505. }
  506. else
  507. {
  508. throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar);
  509. }
  510. default:
  511. if (char.IsWhiteSpace(_currentChar))
  512. {
  513. // eat
  514. }
  515. else if (char.IsDigit(_currentChar) || _currentChar == '-' || _currentChar == '.' || _currentChar == 'N' || _currentChar == 'I')
  516. {
  517. ParseNumber();
  518. return true;
  519. }
  520. else
  521. {
  522. throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar);
  523. }
  524. break;
  525. }
  526. } while (MoveNext());
  527. return false;
  528. }
  529. private bool EatWhitespace(bool oneOrMore)
  530. {
  531. bool whitespace = false;
  532. while (char.IsWhiteSpace(_currentChar))
  533. {
  534. whitespace = true;
  535. MoveNext();
  536. }
  537. return (!oneOrMore || whitespace);
  538. }
  539. private void ParseConstructor()
  540. {
  541. if (MatchValue("new", true))
  542. {
  543. if (EatWhitespace(true))
  544. {
  545. while (char.IsLetter(_currentChar))
  546. {
  547. _buffer.Append(_currentChar);
  548. MoveNext();
  549. }
  550. string constructorName = _buffer.ToString();
  551. _buffer.Length = 0;
  552. List<object> parameters = new List<object>();
  553. EatWhitespace(false);
  554. if (_currentChar == '(' && MoveNext())
  555. {
  556. _currentState = State.Constructor;
  557. while (ParseValue())
  558. {
  559. parameters.Add(_value);
  560. _currentState = State.Constructor;
  561. }
  562. if (string.CompareOrdinal(constructorName, "Date") == 0)
  563. {
  564. long javaScriptTicks = Convert.ToInt64(parameters[0]);
  565. DateTime date = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
  566. SetToken(JsonToken.Date, date);
  567. }
  568. else
  569. {
  570. JavaScriptConstructor constructor = new JavaScriptConstructor(constructorName, new JavaScriptParameters(parameters));
  571. if (_currentState == State.ConstructorEnd)
  572. {
  573. SetToken(JsonToken.Constructor, constructor);
  574. }
  575. }
  576. // move past ')'
  577. MoveNext();
  578. }
  579. }
  580. }
  581. }
  582. private void ParseNumber()
  583. {
  584. // parse until seperator character or end
  585. bool end = false;
  586. bool onlyDigits = true;
  587. if (_currentChar == '-') {
  588. _buffer.Append (_currentChar);
  589. onlyDigits = MoveNext ();
  590. }
  591. if (onlyDigits)
  592. do {
  593. if (CurrentIsSeperator ())
  594. end = true;
  595. else {
  596. _buffer.Append (_currentChar);
  597. if (onlyDigits)
  598. onlyDigits = char.IsDigit (_currentChar);
  599. }
  600. } while (!end && MoveNext ());
  601. string number = _buffer.ToString();
  602. object numberValue;
  603. JsonToken numberType;
  604. if (onlyDigits)
  605. {
  606. if (_buffer.Length <= 9) //should be parsable to Int32
  607. numberValue = Convert.ToInt32 (number, CultureInfo.InvariantCulture);
  608. else if (_buffer.Length <= 18) //should be parsable to Int64
  609. numberValue = Convert.ToInt64 (number, CultureInfo.InvariantCulture);
  610. else
  611. numberValue = Convert.ToDecimal (number, CultureInfo.InvariantCulture);
  612. numberType = JsonToken.Integer;
  613. }
  614. else
  615. {
  616. numberValue = Convert.ToDouble (number, CultureInfo.InvariantCulture);
  617. numberType = JsonToken.Float;
  618. }
  619. _buffer.Length = 0;
  620. SetToken(numberType, numberValue);
  621. }
  622. private void ValidateEnd(JsonToken endToken)
  623. {
  624. JsonType currentObject = Pop();
  625. if (GetTypeForCloseToken(endToken) != currentObject)
  626. throw new JsonReaderException(string.Format("JsonToken {0} is not valid for closing JsonType {1}.", endToken, currentObject));
  627. }
  628. private void SetStateBasedOnCurrent()
  629. {
  630. JsonType currentObject = Peek();
  631. switch (currentObject)
  632. {
  633. case JsonType.Object:
  634. _currentState = State.Object;
  635. break;
  636. case JsonType.Array:
  637. _currentState = State.Array;
  638. break;
  639. case JsonType.None:
  640. _currentState = State.Finished;
  641. break;
  642. default:
  643. throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: " + currentObject);
  644. }
  645. }
  646. private JsonType GetTypeForCloseToken(JsonToken token)
  647. {
  648. switch (token)
  649. {
  650. case JsonToken.EndObject:
  651. return JsonType.Object;
  652. case JsonToken.EndArray:
  653. return JsonType.Array;
  654. default:
  655. throw new JsonReaderException("Not a valid close JsonToken: " + token);
  656. }
  657. }
  658. private void ParseComment()
  659. {
  660. // should have already parsed / character before reaching this method
  661. MoveNext();
  662. if (_currentChar == '*')
  663. {
  664. while (MoveNext())
  665. {
  666. if (_currentChar == '*')
  667. {
  668. if (MoveNext())
  669. {
  670. if (_currentChar == '/')
  671. {
  672. break;
  673. }
  674. else
  675. {
  676. _buffer.Append('*');
  677. _buffer.Append(_currentChar);
  678. }
  679. }
  680. }
  681. else
  682. {
  683. _buffer.Append(_currentChar);
  684. }
  685. }
  686. }
  687. else
  688. {
  689. throw new JsonReaderException("Error parsing comment. Expected: *");
  690. }
  691. SetToken(JsonToken.Comment, _buffer.ToString());
  692. _buffer.Length = 0;
  693. ClearCurrentChar();
  694. }
  695. private bool MatchValue(string value)
  696. {
  697. int i = 0;
  698. do
  699. {
  700. if (_currentChar != value[i])
  701. {
  702. break;
  703. }
  704. i++;
  705. }
  706. while (i < value.Length && MoveNext());
  707. return (i == value.Length);
  708. }
  709. private bool MatchValue(string value, bool noTrailingNonSeperatorCharacters)
  710. {
  711. // will match value and then move to the next character, checking that it is a seperator character
  712. bool match = MatchValue(value);
  713. if (!noTrailingNonSeperatorCharacters)
  714. return match;
  715. else
  716. return (match && (!MoveNext() || CurrentIsSeperator()));
  717. }
  718. private bool CurrentIsSeperator()
  719. {
  720. switch (_currentChar)
  721. {
  722. case '}':
  723. case ']':
  724. case ',':
  725. return true;
  726. case '/':
  727. // check next character to see if start of a comment
  728. return (HasNext() && PeekNext() == '*');
  729. case ')':
  730. if (_currentState == State.Constructor)
  731. return true;
  732. break;
  733. default:
  734. if (char.IsWhiteSpace(_currentChar))
  735. return true;
  736. break;
  737. }
  738. return false;
  739. }
  740. private void ParseTrue()
  741. {
  742. // check characters equal 'true'
  743. // and that it is followed by either a seperator character
  744. // or the text ends
  745. if (MatchValue(JavaScriptConvert.True, true))
  746. {
  747. SetToken(JsonToken.Boolean, true);
  748. }
  749. else
  750. {
  751. throw new JsonReaderException("Error parsing boolean value.");
  752. }
  753. }
  754. private void ParseNull()
  755. {
  756. if (MatchValue(JavaScriptConvert.Null, true))
  757. {
  758. SetToken(JsonToken.Null);
  759. }
  760. else
  761. {
  762. throw new JsonReaderException("Error parsing null value.");
  763. }
  764. }
  765. private void ParseUndefined()
  766. {
  767. if (MatchValue(JavaScriptConvert.Undefined, true))
  768. {
  769. SetToken(JsonToken.Undefined);
  770. }
  771. else
  772. {
  773. throw new JsonReaderException("Error parsing undefined value.");
  774. }
  775. }
  776. private void ParseFalse()
  777. {
  778. if (MatchValue(JavaScriptConvert.False, true))
  779. {
  780. SetToken(JsonToken.Boolean, false);
  781. }
  782. else
  783. {
  784. throw new JsonReaderException("Error parsing boolean value.");
  785. }
  786. }
  787. void IDisposable.Dispose()
  788. {
  789. Dispose(true);
  790. }
  791. private void Dispose(bool disposing)
  792. {
  793. if (_currentState != State.Closed && disposing)
  794. Close();
  795. }
  796. /// <summary>
  797. /// Changes the <see cref="State"/> to Closed.
  798. /// </summary>
  799. public void Close()
  800. {
  801. _currentState = State.Closed;
  802. _token = JsonToken.None;
  803. _value = null;
  804. _valueType = null;
  805. if (_reader != null)
  806. _reader.Close();
  807. _buffer = null;
  808. }
  809. }
  810. }