JsonReader.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  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. }
  127. return read_state != ReadState.Interactive ? 0 : elements.Count - 1 + mod;
  128. }
  129. }
  130. public override bool EOF {
  131. get {
  132. switch (read_state) {
  133. case ReadState.Closed:
  134. case ReadState.EndOfFile:
  135. return true;
  136. default:
  137. return false;
  138. }
  139. }
  140. }
  141. public override bool HasValue {
  142. get {
  143. switch (NodeType) {
  144. case XmlNodeType.Attribute:
  145. case XmlNodeType.Text:
  146. return true;
  147. default:
  148. return false;
  149. }
  150. }
  151. }
  152. public override bool IsEmptyElement {
  153. get { return false; }
  154. }
  155. public override string LocalName {
  156. get {
  157. switch (attr_state) {
  158. case AttributeState.Type:
  159. return "type";
  160. case AttributeState.RuntimeType:
  161. return "__type";
  162. }
  163. switch (NodeType) {
  164. case XmlNodeType.Element:
  165. case XmlNodeType.EndElement:
  166. return elements.Peek ().Name;
  167. default:
  168. return String.Empty;
  169. }
  170. }
  171. }
  172. public override string NamespaceURI {
  173. get { return String.Empty; }
  174. }
  175. public override XmlNameTable NameTable {
  176. get { return name_table; }
  177. }
  178. public override XmlNodeType NodeType {
  179. get {
  180. switch (attr_state) {
  181. case AttributeState.Type:
  182. case AttributeState.RuntimeType:
  183. return XmlNodeType.Attribute;
  184. case AttributeState.TypeValue:
  185. case AttributeState.RuntimeTypeValue:
  186. return XmlNodeType.Text;
  187. default:
  188. return current_node;
  189. }
  190. }
  191. }
  192. public override string Prefix {
  193. get { return String.Empty; }
  194. }
  195. public override ReadState ReadState {
  196. get { return read_state; }
  197. }
  198. public override string Value {
  199. get {
  200. switch (attr_state) {
  201. case AttributeState.Type:
  202. case AttributeState.TypeValue:
  203. return elements.Peek ().Type;
  204. case AttributeState.RuntimeType:
  205. case AttributeState.RuntimeTypeValue:
  206. return current_runtime_type;
  207. default:
  208. return current_node == XmlNodeType.Text ? simple_value : String.Empty;
  209. }
  210. }
  211. }
  212. public override void Close ()
  213. {
  214. if (on_close != null) {
  215. on_close (this);
  216. on_close = null;
  217. }
  218. read_state = ReadState.Closed;
  219. }
  220. public override string GetAttribute (int index)
  221. {
  222. if (index == 0 && current_node == XmlNodeType.Element)
  223. return elements.Peek ().Type;
  224. else if (index == 1 && current_runtime_type != null)
  225. return current_runtime_type;
  226. 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");
  227. }
  228. public override string GetAttribute (string name)
  229. {
  230. if (current_node != XmlNodeType.Element)
  231. return null;
  232. switch (name) {
  233. case "type":
  234. return elements.Peek ().Type;
  235. case "__type":
  236. return current_runtime_type;
  237. default:
  238. return null;
  239. }
  240. }
  241. public override string GetAttribute (string localName, string ns)
  242. {
  243. if (ns == String.Empty)
  244. return GetAttribute (localName);
  245. else
  246. return null;
  247. }
  248. public override string LookupNamespace (string prefix)
  249. {
  250. if (prefix == null)
  251. throw new ArgumentNullException ("prefix");
  252. else if (prefix.Length == 0)
  253. return String.Empty;
  254. return null;
  255. }
  256. public override bool MoveToAttribute (string name)
  257. {
  258. if (current_node != XmlNodeType.Element)
  259. return false;
  260. switch (name) {
  261. case "type":
  262. attr_state = AttributeState.Type;
  263. return true;
  264. case "__type":
  265. if (current_runtime_type == null)
  266. return false;
  267. attr_state = AttributeState.RuntimeType;
  268. return true;
  269. default:
  270. return false;
  271. }
  272. }
  273. public override bool MoveToAttribute (string localName, string ns)
  274. {
  275. if (ns != String.Empty)
  276. return false;
  277. return MoveToAttribute (localName);
  278. }
  279. public override bool MoveToElement ()
  280. {
  281. if (attr_state == AttributeState.None)
  282. return false;
  283. attr_state = AttributeState.None;
  284. return true;
  285. }
  286. public override bool MoveToFirstAttribute ()
  287. {
  288. if (current_node != XmlNodeType.Element)
  289. return false;
  290. attr_state = AttributeState.Type;
  291. return true;
  292. }
  293. public override bool MoveToNextAttribute ()
  294. {
  295. if (attr_state == AttributeState.None)
  296. return MoveToFirstAttribute ();
  297. else
  298. return MoveToAttribute ("__type");
  299. }
  300. public override bool ReadAttributeValue ()
  301. {
  302. switch (attr_state) {
  303. case AttributeState.Type:
  304. attr_state = AttributeState.TypeValue;
  305. return true;
  306. case AttributeState.RuntimeType:
  307. attr_state = AttributeState.RuntimeTypeValue;
  308. return true;
  309. }
  310. return false;
  311. }
  312. public override void ResolveEntity ()
  313. {
  314. throw new NotSupportedException ();
  315. }
  316. public override bool Read ()
  317. {
  318. switch (read_state) {
  319. case ReadState.EndOfFile:
  320. case ReadState.Closed:
  321. case ReadState.Error:
  322. return false;
  323. case ReadState.Initial:
  324. read_state = ReadState.Interactive;
  325. next_element = "root";
  326. current_node = XmlNodeType.Element;
  327. break;
  328. }
  329. if (content_stored) {
  330. if (current_node == XmlNodeType.Element) {
  331. if (elements.Peek ().Type == "null") {
  332. // since null is not consumed as text content, it skips Text state.
  333. current_node = XmlNodeType.EndElement;
  334. content_stored = false;
  335. }
  336. else
  337. current_node = XmlNodeType.Text;
  338. return true;
  339. } else if (current_node == XmlNodeType.Text) {
  340. current_node = XmlNodeType.EndElement;
  341. content_stored = false;
  342. return true;
  343. }
  344. }
  345. else if (current_node == XmlNodeType.EndElement) {
  346. // clear EndElement state
  347. elements.Pop ();
  348. if (elements.Count > 0)
  349. elements.Peek ().HasContent = true;
  350. else
  351. finished = true;
  352. }
  353. SkipWhitespaces ();
  354. attr_state = AttributeState.None;
  355. // Default. May be overriden only as EndElement or None.
  356. current_node = XmlNodeType.Element;
  357. if (!ReadContent (false))
  358. return false;
  359. if (finished)
  360. throw XmlError ("Multiple top-level content is not allowed");
  361. return true;
  362. }
  363. bool ReadContent (bool objectValue)
  364. {
  365. int ch = ReadChar ();
  366. if (ch < 0) {
  367. ReadEndOfStream ();
  368. return false;
  369. }
  370. bool itemMustFollow = false;
  371. if (!objectValue && elements.Count > 0 && elements.Peek ().HasContent) {
  372. if (ch == ',') {
  373. switch (elements.Peek ().Type) {
  374. case "object":
  375. case "array":
  376. SkipWhitespaces ();
  377. ch = ReadChar ();
  378. itemMustFollow = true;
  379. break;
  380. }
  381. }
  382. else if (ch != '}' && ch != ']')
  383. throw XmlError ("Comma is required unless an array or object is at the end");
  384. }
  385. if (elements.Count > 0 && elements.Peek ().Type == "array")
  386. next_element = "item";
  387. else if (next_object_content_name != null) {
  388. next_element = next_object_content_name;
  389. next_object_content_name = null;
  390. if (ch != ':')
  391. throw XmlError ("':' is expected after a name of an object content");
  392. SkipWhitespaces ();
  393. ReadContent (true);
  394. return true;
  395. }
  396. switch (ch) {
  397. case '{':
  398. ReadStartObject ();
  399. return true;
  400. case '[':
  401. ReadStartArray ();
  402. return true;
  403. case '}':
  404. if (itemMustFollow)
  405. throw XmlError ("Invalid comma before an end of object");
  406. if (objectValue)
  407. throw XmlError ("Invalid end of object as an object content");
  408. ReadEndObject ();
  409. return true;
  410. case ']':
  411. if (itemMustFollow)
  412. throw XmlError ("Invalid comma before an end of array");
  413. if (objectValue)
  414. throw XmlError ("Invalid end of array as an object content");
  415. ReadEndArray ();
  416. return true;
  417. case '"':
  418. string s = ReadStringLiteral ();
  419. if (!objectValue && elements.Count > 0 && elements.Peek ().Type == "object") {
  420. next_element = s;
  421. SkipWhitespaces ();
  422. Expect (':');
  423. SkipWhitespaces ();
  424. ReadContent (true);
  425. }
  426. else
  427. ReadAsSimpleContent ("string", s);
  428. return true;
  429. case '-':
  430. ReadNumber (ch);
  431. return true;
  432. case 'n':
  433. if (ReadChar () == 'u' &&
  434. ReadChar () == 'l' &&
  435. ReadChar () == 'l') {
  436. ReadAsSimpleContent ("null", "null");
  437. return true;
  438. }
  439. goto default;
  440. case 't':
  441. if (ReadChar () == 'r' &&
  442. ReadChar () == 'u' &&
  443. ReadChar () == 'e') {
  444. ReadAsSimpleContent ("boolean", "true");
  445. return true;
  446. }
  447. goto default;
  448. case 'f':
  449. if (ReadChar () == 'a' &&
  450. ReadChar () == 'l' &&
  451. ReadChar () == 's' &&
  452. ReadChar () == 'e') {
  453. ReadAsSimpleContent ("boolean", "false");
  454. return true;
  455. }
  456. goto default;
  457. default:
  458. if ('0' <= ch && ch <= '9') {
  459. ReadNumber (ch);
  460. return true;
  461. }
  462. throw XmlError ("Unexpected token");
  463. }
  464. }
  465. void ReadStartObject ()
  466. {
  467. ElementInfo ei = new ElementInfo (next_element, "object");
  468. elements.Push (ei);
  469. SkipWhitespaces ();
  470. if (PeekChar () == '"') { // it isn't premise: the object might be empty
  471. ReadChar ();
  472. string s = ReadStringLiteral ();
  473. if (s == "__type") {
  474. SkipWhitespaces ();
  475. Expect (':');
  476. SkipWhitespaces ();
  477. Expect ('"');
  478. current_runtime_type = ReadStringLiteral ();
  479. SkipWhitespaces ();
  480. ei.HasContent = true;
  481. }
  482. else
  483. next_object_content_name = s;
  484. }
  485. }
  486. void ReadStartArray ()
  487. {
  488. elements.Push (new ElementInfo (next_element, "array"));
  489. }
  490. void ReadEndObject ()
  491. {
  492. if (elements.Count == 0 || elements.Peek ().Type != "object")
  493. throw XmlError ("Unexpected end of object");
  494. current_node = XmlNodeType.EndElement;
  495. }
  496. void ReadEndArray ()
  497. {
  498. if (elements.Count == 0 || elements.Peek ().Type != "array")
  499. throw XmlError ("Unexpected end of array");
  500. current_node = XmlNodeType.EndElement;
  501. }
  502. void ReadEndOfStream ()
  503. {
  504. if (elements.Count > 0)
  505. throw XmlError (String.Format ("{0} missing end of arrays or objects", elements.Count));
  506. read_state = ReadState.EndOfFile;
  507. current_node = XmlNodeType.None;
  508. }
  509. void ReadAsSimpleContent (string type, string value)
  510. {
  511. elements.Push (new ElementInfo (next_element, type));
  512. simple_value = value;
  513. content_stored = true;
  514. }
  515. void ReadNumber (int ch)
  516. {
  517. elements.Push (new ElementInfo (next_element, "number"));
  518. content_stored = true;
  519. int init = ch;
  520. int prev;
  521. bool floating = false, exp = false;
  522. StringBuilder sb = new StringBuilder ();
  523. bool cont = true;
  524. do {
  525. sb.Append ((char) ch);
  526. prev = ch;
  527. ch = ReadChar ();
  528. if (prev == '-' && !IsNumber (ch)) // neither '.', '-' or '+' nor anything else is valid
  529. throw XmlError ("Invalid JSON number");
  530. switch (ch) {
  531. case 'e':
  532. case 'E':
  533. if (exp)
  534. throw XmlError ("Invalid JSON number token. Either 'E' or 'e' must not occur more than once");
  535. if (!IsNumber (prev))
  536. throw XmlError ("Invalid JSON number token. only a number is valid before 'E' or 'e'");
  537. exp = true;
  538. break;
  539. case '.':
  540. if (floating)
  541. throw XmlError ("Invalid JSON number token. '.' must not occur twice");
  542. if (exp)
  543. throw XmlError ("Invalid JSON number token. '.' must not occur after 'E' or 'e'");
  544. floating = true;
  545. break;
  546. default:
  547. if (!IsNumber (ch)) {
  548. saved_char = ch;
  549. cont = false;
  550. }
  551. break;
  552. }
  553. } while (cont);
  554. if (!IsNumber (prev)) // only number is valid at the end
  555. throw XmlError ("Invalid JSON number");
  556. simple_value = sb.ToString ();
  557. if (init == '0' && !floating && !exp && simple_value != "0")
  558. throw XmlError ("Invalid JSON number");
  559. }
  560. bool IsNumber (int c)
  561. {
  562. return '0' <= c && c <= '9';
  563. }
  564. // FIXME: implement
  565. string ReadStringLiteral ()
  566. {
  567. StringBuilder sb = new StringBuilder ();
  568. do {
  569. int ch = ReadChar ();
  570. if (ch < 0)
  571. throw XmlError ("Unexpected end of stream in string literal");
  572. if (ch == '"')
  573. return sb.ToString ();
  574. sb.Append ((char) ch);
  575. } while (true);
  576. }
  577. int PeekChar ()
  578. {
  579. if (saved_char < 0)
  580. saved_char = reader.Read ();
  581. return saved_char;
  582. }
  583. int ReadChar ()
  584. {
  585. int v = saved_char >= 0 ? saved_char : reader.Read ();
  586. saved_char = -1;
  587. if (v == '\n') {
  588. line++;
  589. column = 0;
  590. }
  591. else
  592. column++;
  593. return v;
  594. }
  595. void SkipWhitespaces ()
  596. {
  597. do {
  598. switch (PeekChar ()) {
  599. case ' ':
  600. case '\t':
  601. case '\r':
  602. case '\n':
  603. ReadChar ();
  604. continue;
  605. default:
  606. return;
  607. }
  608. } while (true);
  609. }
  610. void Expect (char c)
  611. {
  612. int v = ReadChar ();
  613. if (v < 0)
  614. throw XmlError (String.Format ("Expected '{0}' but got EOF", c));
  615. if (v != c)
  616. throw XmlError (String.Format ("Expected '{0}' but got '{1}'", c, (char) v));
  617. }
  618. Exception XmlError (string s)
  619. {
  620. return new XmlException (String.Format ("{0} ({1},{2})", s, line, column));
  621. }
  622. }
  623. }