2
0

JsonReader.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. //
  2. // JsonWriter.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Globalization;
  31. using System.IO;
  32. using System.Text;
  33. using System.Xml;
  34. namespace System.Runtime.Serialization.Json
  35. {
  36. // FIXME: quotas check
  37. // FIXME: parse object content and handle __type as attribute.
  38. class JsonReader : XmlDictionaryReader, IXmlJsonReaderInitializer, IXmlLineInfo
  39. {
  40. class ElementInfo
  41. {
  42. public readonly string Name;
  43. public readonly string Type;
  44. public bool HasContent;
  45. public ElementInfo (string name, string type)
  46. {
  47. this.Name = name;
  48. this.Type = type;
  49. }
  50. }
  51. enum AttributeState
  52. {
  53. None,
  54. Type,
  55. TypeValue,
  56. RuntimeType,
  57. RuntimeTypeValue
  58. }
  59. TextReader reader;
  60. XmlDictionaryReaderQuotas quotas;
  61. OnXmlDictionaryReaderClose on_close;
  62. XmlNameTable name_table = new NameTable ();
  63. XmlNodeType current_node;
  64. AttributeState attr_state;
  65. string simple_value;
  66. string next_element;
  67. string current_runtime_type, next_object_content_name;
  68. ReadState read_state = ReadState.Initial;
  69. bool content_stored;
  70. bool finished;
  71. Stack<ElementInfo> elements = new Stack<ElementInfo> ();
  72. int line = 1, column = 0;
  73. int saved_char = -1;
  74. // Constructors
  75. public JsonReader (byte [] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
  76. {
  77. SetInput (buffer, offset, count, encoding, quotas, onClose);
  78. }
  79. public JsonReader (Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
  80. {
  81. SetInput (stream, encoding, quotas, onClose);
  82. }
  83. // IXmlLineInfo
  84. public bool HasLineInfo ()
  85. {
  86. return true;
  87. }
  88. public int LineNumber {
  89. get { return line; }
  90. }
  91. public int LinePosition {
  92. get { return column; }
  93. }
  94. // IXmlJsonReaderInitializer
  95. public void SetInput (byte [] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
  96. {
  97. SetInput (new MemoryStream (buffer, offset, count), encoding, quotas, onClose);
  98. }
  99. public void SetInput (Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
  100. {
  101. reader = new StreamReader (stream, encoding ?? Encoding.UTF8);
  102. if (quotas == null)
  103. throw new ArgumentNullException ("quotas");
  104. this.quotas = quotas;
  105. this.on_close = onClose;
  106. }
  107. // XmlDictionaryReader
  108. public override int AttributeCount {
  109. get { return current_node != XmlNodeType.Element ? 0 : current_runtime_type != null ? 2 : 1; }
  110. }
  111. public override string BaseURI {
  112. get { return String.Empty; }
  113. }
  114. public override int Depth {
  115. get {
  116. int mod = 0;
  117. switch (attr_state) {
  118. case AttributeState.Type:
  119. case AttributeState.RuntimeType:
  120. mod++;
  121. break;
  122. case AttributeState.TypeValue:
  123. case AttributeState.RuntimeTypeValue:
  124. mod += 2;
  125. break;
  126. case AttributeState.None:
  127. if (NodeType == XmlNodeType.Text)
  128. mod++;
  129. break;
  130. }
  131. return read_state != ReadState.Interactive ? 0 : elements.Count - 1 + mod;
  132. }
  133. }
  134. public override bool EOF {
  135. get {
  136. switch (read_state) {
  137. case ReadState.Closed:
  138. case ReadState.EndOfFile:
  139. return true;
  140. default:
  141. return false;
  142. }
  143. }
  144. }
  145. public override bool HasValue {
  146. get {
  147. switch (NodeType) {
  148. case XmlNodeType.Attribute:
  149. case XmlNodeType.Text:
  150. return true;
  151. default:
  152. return false;
  153. }
  154. }
  155. }
  156. public override bool IsEmptyElement {
  157. get { return false; }
  158. }
  159. public override string LocalName {
  160. get {
  161. switch (attr_state) {
  162. case AttributeState.Type:
  163. return "type";
  164. case AttributeState.RuntimeType:
  165. return "__type";
  166. }
  167. switch (NodeType) {
  168. case XmlNodeType.Element:
  169. case XmlNodeType.EndElement:
  170. return elements.Peek ().Name;
  171. default:
  172. return String.Empty;
  173. }
  174. }
  175. }
  176. public override string NamespaceURI {
  177. get { return String.Empty; }
  178. }
  179. public override XmlNameTable NameTable {
  180. get { return name_table; }
  181. }
  182. public override XmlNodeType NodeType {
  183. get {
  184. switch (attr_state) {
  185. case AttributeState.Type:
  186. case AttributeState.RuntimeType:
  187. return XmlNodeType.Attribute;
  188. case AttributeState.TypeValue:
  189. case AttributeState.RuntimeTypeValue:
  190. return XmlNodeType.Text;
  191. default:
  192. return current_node;
  193. }
  194. }
  195. }
  196. public override string Prefix {
  197. get { return String.Empty; }
  198. }
  199. public override ReadState ReadState {
  200. get { return read_state; }
  201. }
  202. public override string Value {
  203. get {
  204. switch (attr_state) {
  205. case AttributeState.Type:
  206. case AttributeState.TypeValue:
  207. return elements.Peek ().Type;
  208. case AttributeState.RuntimeType:
  209. case AttributeState.RuntimeTypeValue:
  210. return current_runtime_type;
  211. default:
  212. return current_node == XmlNodeType.Text ? simple_value : String.Empty;
  213. }
  214. }
  215. }
  216. public override void Close ()
  217. {
  218. if (on_close != null) {
  219. on_close (this);
  220. on_close = null;
  221. }
  222. read_state = ReadState.Closed;
  223. }
  224. public override string GetAttribute (int index)
  225. {
  226. if (index == 0 && current_node == XmlNodeType.Element)
  227. return elements.Peek ().Type;
  228. else if (index == 1 && current_runtime_type != null)
  229. return current_runtime_type;
  230. throw new ArgumentOutOfRangeException ("index", "Index is must be either 0 or 1 when there is an explicit __type in the object, and only valid on an element on this XmlDictionaryReader");
  231. }
  232. public override string GetAttribute (string name)
  233. {
  234. if (current_node != XmlNodeType.Element)
  235. return null;
  236. switch (name) {
  237. case "type":
  238. return elements.Peek ().Type;
  239. case "__type":
  240. return current_runtime_type;
  241. default:
  242. return null;
  243. }
  244. }
  245. public override string GetAttribute (string localName, string ns)
  246. {
  247. if (ns == String.Empty)
  248. return GetAttribute (localName);
  249. else
  250. return null;
  251. }
  252. public override string LookupNamespace (string prefix)
  253. {
  254. if (prefix == null)
  255. throw new ArgumentNullException ("prefix");
  256. else if (prefix.Length == 0)
  257. return String.Empty;
  258. return null;
  259. }
  260. public override bool MoveToAttribute (string name)
  261. {
  262. if (current_node != XmlNodeType.Element)
  263. return false;
  264. switch (name) {
  265. case "type":
  266. attr_state = AttributeState.Type;
  267. return true;
  268. case "__type":
  269. if (current_runtime_type == null)
  270. return false;
  271. attr_state = AttributeState.RuntimeType;
  272. return true;
  273. default:
  274. return false;
  275. }
  276. }
  277. public override bool MoveToAttribute (string localName, string ns)
  278. {
  279. if (ns != String.Empty)
  280. return false;
  281. return MoveToAttribute (localName);
  282. }
  283. public override bool MoveToElement ()
  284. {
  285. if (attr_state == AttributeState.None)
  286. return false;
  287. attr_state = AttributeState.None;
  288. return true;
  289. }
  290. public override bool MoveToFirstAttribute ()
  291. {
  292. if (current_node != XmlNodeType.Element)
  293. return false;
  294. attr_state = AttributeState.Type;
  295. return true;
  296. }
  297. public override bool MoveToNextAttribute ()
  298. {
  299. if (attr_state == AttributeState.None)
  300. return MoveToFirstAttribute ();
  301. else
  302. return MoveToAttribute ("__type");
  303. }
  304. public override bool ReadAttributeValue ()
  305. {
  306. switch (attr_state) {
  307. case AttributeState.Type:
  308. attr_state = AttributeState.TypeValue;
  309. return true;
  310. case AttributeState.RuntimeType:
  311. attr_state = AttributeState.RuntimeTypeValue;
  312. return true;
  313. }
  314. return false;
  315. }
  316. public override void ResolveEntity ()
  317. {
  318. throw new NotSupportedException ();
  319. }
  320. public override bool Read ()
  321. {
  322. switch (read_state) {
  323. case ReadState.EndOfFile:
  324. case ReadState.Closed:
  325. case ReadState.Error:
  326. return false;
  327. case ReadState.Initial:
  328. read_state = ReadState.Interactive;
  329. next_element = "root";
  330. current_node = XmlNodeType.Element;
  331. break;
  332. }
  333. if (content_stored) {
  334. if (current_node == XmlNodeType.Element) {
  335. if (elements.Peek ().Type == "null") {
  336. // since null is not consumed as text content, it skips Text state.
  337. current_node = XmlNodeType.EndElement;
  338. content_stored = false;
  339. }
  340. else
  341. current_node = XmlNodeType.Text;
  342. return true;
  343. } else if (current_node == XmlNodeType.Text) {
  344. current_node = XmlNodeType.EndElement;
  345. content_stored = false;
  346. return true;
  347. }
  348. }
  349. else if (current_node == XmlNodeType.EndElement) {
  350. // clear EndElement state
  351. elements.Pop ();
  352. if (elements.Count > 0)
  353. elements.Peek ().HasContent = true;
  354. else
  355. finished = true;
  356. }
  357. SkipWhitespaces ();
  358. attr_state = AttributeState.None;
  359. // Default. May be overriden only as EndElement or None.
  360. current_node = XmlNodeType.Element;
  361. if (!ReadContent (false))
  362. return false;
  363. if (finished)
  364. throw XmlError ("Multiple top-level content is not allowed");
  365. return true;
  366. }
  367. bool ReadContent (bool objectValue)
  368. {
  369. int ch = ReadChar ();
  370. if (ch < 0) {
  371. ReadEndOfStream ();
  372. return false;
  373. }
  374. bool itemMustFollow = false;
  375. if (!objectValue && elements.Count > 0 && elements.Peek ().HasContent) {
  376. if (ch == ',') {
  377. switch (elements.Peek ().Type) {
  378. case "object":
  379. case "array":
  380. SkipWhitespaces ();
  381. ch = ReadChar ();
  382. itemMustFollow = true;
  383. break;
  384. }
  385. }
  386. else if (ch != '}' && ch != ']')
  387. throw XmlError ("Comma is required unless an array or object is at the end");
  388. }
  389. if (elements.Count > 0 && elements.Peek ().Type == "array")
  390. next_element = "item";
  391. else if (next_object_content_name != null) {
  392. next_element = next_object_content_name;
  393. next_object_content_name = null;
  394. if (ch != ':')
  395. throw XmlError ("':' is expected after a name of an object content");
  396. SkipWhitespaces ();
  397. ReadContent (true);
  398. return true;
  399. }
  400. switch (ch) {
  401. case '{':
  402. ReadStartObject ();
  403. return true;
  404. case '[':
  405. ReadStartArray ();
  406. return true;
  407. case '}':
  408. if (itemMustFollow)
  409. throw XmlError ("Invalid comma before an end of object");
  410. if (objectValue)
  411. throw XmlError ("Invalid end of object as an object content");
  412. ReadEndObject ();
  413. return true;
  414. case ']':
  415. if (itemMustFollow)
  416. throw XmlError ("Invalid comma before an end of array");
  417. if (objectValue)
  418. throw XmlError ("Invalid end of array as an object content");
  419. ReadEndArray ();
  420. return true;
  421. case '"':
  422. string s = ReadStringLiteral ();
  423. if (!objectValue && elements.Count > 0 && elements.Peek ().Type == "object") {
  424. next_element = s;
  425. SkipWhitespaces ();
  426. Expect (':');
  427. SkipWhitespaces ();
  428. ReadContent (true);
  429. }
  430. else
  431. ReadAsSimpleContent ("string", s);
  432. return true;
  433. case '-':
  434. ReadNumber (ch);
  435. return true;
  436. case 'n':
  437. if (ReadChar () == 'u' &&
  438. ReadChar () == 'l' &&
  439. ReadChar () == 'l') {
  440. ReadAsSimpleContent ("null", "null");
  441. return true;
  442. }
  443. goto default;
  444. case 't':
  445. if (ReadChar () == 'r' &&
  446. ReadChar () == 'u' &&
  447. ReadChar () == 'e') {
  448. ReadAsSimpleContent ("boolean", "true");
  449. return true;
  450. }
  451. goto default;
  452. case 'f':
  453. if (ReadChar () == 'a' &&
  454. ReadChar () == 'l' &&
  455. ReadChar () == 's' &&
  456. ReadChar () == 'e') {
  457. ReadAsSimpleContent ("boolean", "false");
  458. return true;
  459. }
  460. goto default;
  461. default:
  462. if ('0' <= ch && ch <= '9') {
  463. ReadNumber (ch);
  464. return true;
  465. }
  466. throw XmlError (String.Format ("Unexpected token: '{0}' ({1:X04})", ch, (int) ch));
  467. }
  468. }
  469. void ReadStartObject ()
  470. {
  471. ElementInfo ei = new ElementInfo (next_element, "object");
  472. elements.Push (ei);
  473. SkipWhitespaces ();
  474. if (PeekChar () == '"') { // it isn't premise: the object might be empty
  475. ReadChar ();
  476. string s = ReadStringLiteral ();
  477. if (s == "__type") {
  478. SkipWhitespaces ();
  479. Expect (':');
  480. SkipWhitespaces ();
  481. Expect ('"');
  482. current_runtime_type = ReadStringLiteral ();
  483. SkipWhitespaces ();
  484. ei.HasContent = true;
  485. }
  486. else
  487. next_object_content_name = s;
  488. }
  489. }
  490. void ReadStartArray ()
  491. {
  492. elements.Push (new ElementInfo (next_element, "array"));
  493. }
  494. void ReadEndObject ()
  495. {
  496. if (elements.Count == 0 || elements.Peek ().Type != "object")
  497. throw XmlError ("Unexpected end of object");
  498. current_node = XmlNodeType.EndElement;
  499. }
  500. void ReadEndArray ()
  501. {
  502. if (elements.Count == 0 || elements.Peek ().Type != "array")
  503. throw XmlError ("Unexpected end of array");
  504. current_node = XmlNodeType.EndElement;
  505. }
  506. void ReadEndOfStream ()
  507. {
  508. if (elements.Count > 0)
  509. throw XmlError (String.Format ("{0} missing end of arrays or objects", elements.Count));
  510. read_state = ReadState.EndOfFile;
  511. current_node = XmlNodeType.None;
  512. }
  513. void ReadAsSimpleContent (string type, string value)
  514. {
  515. elements.Push (new ElementInfo (next_element, type));
  516. simple_value = value;
  517. content_stored = true;
  518. }
  519. void ReadNumber (int ch)
  520. {
  521. elements.Push (new ElementInfo (next_element, "number"));
  522. content_stored = true;
  523. int init = ch;
  524. int prev;
  525. bool floating = false, exp = false;
  526. StringBuilder sb = new StringBuilder ();
  527. bool cont = true;
  528. do {
  529. sb.Append ((char) ch);
  530. prev = ch;
  531. ch = ReadChar ();
  532. if (prev == '-' && !IsNumber (ch)) // neither '.', '-' or '+' nor anything else is valid
  533. throw XmlError ("Invalid JSON number");
  534. switch (ch) {
  535. case 'e':
  536. case 'E':
  537. if (exp)
  538. throw XmlError ("Invalid JSON number token. Either 'E' or 'e' must not occur more than once");
  539. if (!IsNumber (prev))
  540. throw XmlError ("Invalid JSON number token. only a number is valid before 'E' or 'e'");
  541. exp = true;
  542. break;
  543. case '.':
  544. if (floating)
  545. throw XmlError ("Invalid JSON number token. '.' must not occur twice");
  546. if (exp)
  547. throw XmlError ("Invalid JSON number token. '.' must not occur after 'E' or 'e'");
  548. floating = true;
  549. break;
  550. default:
  551. if (!IsNumber (ch)) {
  552. saved_char = ch;
  553. cont = false;
  554. }
  555. break;
  556. }
  557. } while (cont);
  558. if (!IsNumber (prev)) // only number is valid at the end
  559. throw XmlError ("Invalid JSON number");
  560. simple_value = sb.ToString ();
  561. if (init == '0' && !floating && !exp && simple_value != "0")
  562. throw XmlError ("Invalid JSON number");
  563. }
  564. bool IsNumber (int c)
  565. {
  566. return '0' <= c && c <= '9';
  567. }
  568. // FIXME: implement
  569. string ReadStringLiteral ()
  570. {
  571. StringBuilder sb = new StringBuilder ();
  572. do {
  573. int ch = ReadChar ();
  574. if (ch < 0)
  575. throw XmlError ("Unexpected end of stream in string literal");
  576. if (ch == '"')
  577. return sb.ToString ();
  578. sb.Append ((char) ch);
  579. } while (true);
  580. }
  581. int PeekChar ()
  582. {
  583. if (saved_char < 0)
  584. saved_char = reader.Read ();
  585. return saved_char;
  586. }
  587. int ReadChar ()
  588. {
  589. int v = saved_char >= 0 ? saved_char : reader.Read ();
  590. saved_char = -1;
  591. if (v == '\n') {
  592. line++;
  593. column = 0;
  594. }
  595. else
  596. column++;
  597. return v;
  598. }
  599. void SkipWhitespaces ()
  600. {
  601. do {
  602. switch (PeekChar ()) {
  603. case ' ':
  604. case '\t':
  605. case '\r':
  606. case '\n':
  607. ReadChar ();
  608. continue;
  609. default:
  610. return;
  611. }
  612. } while (true);
  613. }
  614. void Expect (char c)
  615. {
  616. int v = ReadChar ();
  617. if (v < 0)
  618. throw XmlError (String.Format ("Expected '{0}' but got EOF", c));
  619. if (v != c)
  620. throw XmlError (String.Format ("Expected '{0}' but got '{1}'", c, (char) v));
  621. }
  622. Exception XmlError (string s)
  623. {
  624. return new XmlException (String.Format ("{0} ({1},{2})", s, line, column));
  625. }
  626. }
  627. }