JsonWriter.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  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 Newtonsoft.Json.Utilities;
  25. using System.Collections;
  26. using System.Web.Script.Serialization;
  27. namespace Newtonsoft.Json
  28. {
  29. internal enum JsonType
  30. {
  31. Object,
  32. Array,
  33. None
  34. }
  35. /// <summary>
  36. /// Specifies the state of the <see cref="JsonWriter"/>.
  37. /// </summary>
  38. enum WriteState
  39. {
  40. /// <summary>
  41. /// An exception has been thrown, which has left the <see cref="JsonWriter"/> in an invalid state.
  42. /// You may call the <see cref="JsonWriter.Close"/> method to put the <see cref="JsonWriter"/> in the <c>Closed</c> state.
  43. /// Any other <see cref="JsonWriter"/> method calls results in an <see cref="InvalidOperationException"/> being thrown.
  44. /// </summary>
  45. Error,
  46. /// <summary>
  47. /// The <see cref="JsonWriter.Close"/> method has been called.
  48. /// </summary>
  49. Closed,
  50. /// <summary>
  51. /// An object is being written.
  52. /// </summary>
  53. Object,
  54. /// <summary>
  55. /// A array is being written.
  56. /// </summary>
  57. Array,
  58. /// <summary>
  59. /// A property is being written.
  60. /// </summary>
  61. Property,
  62. /// <summary>
  63. /// A write method has not been called.
  64. /// </summary>
  65. Start
  66. }
  67. /// <summary>
  68. /// Specifies formatting options for the <see cref="JsonWriter"/>.
  69. /// </summary>
  70. enum Formatting
  71. {
  72. /// <summary>
  73. /// No special formatting is applied. This is the default.
  74. /// </summary>
  75. None,
  76. /// <summary>
  77. /// Causes child objects to be indented according to the <see cref="JsonWriter.Indentation"/> and <see cref="JsonWriter.IndentChar"/> settings.
  78. /// </summary>
  79. Indented
  80. }
  81. /// <summary>
  82. /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data.
  83. /// </summary>
  84. sealed class JsonWriter : IDisposable
  85. {
  86. private enum State
  87. {
  88. Start,
  89. Property,
  90. ObjectStart,
  91. Object,
  92. ArrayStart,
  93. Array,
  94. Closed,
  95. Error
  96. }
  97. // array that gives a new state based on the current state an the token being written
  98. private static readonly State[,] stateArray = {
  99. // Start PropertyName ObjectStart Object ArrayStart Array Closed Error
  100. //
  101. /* None */{ State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error },
  102. /* StartObject */{ State.ObjectStart, State.ObjectStart, State.Error, State.Error, State.ObjectStart, State.ObjectStart, State.Error, State.Error },
  103. /* StartArray */{ State.ArrayStart, State.ArrayStart, State.Error, State.Error, State.ArrayStart, State.ArrayStart, State.Error, State.Error },
  104. /* StartProperty */{ State.Error, State.Error, State.Property, State.Property, State.Error, State.Error, State.Error, State.Error },
  105. /* Comment */{ State.Error, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Error, State.Error },
  106. /* Value */{ State.Closed, State.Object, State.Error, State.Error, State.Array, State.Array, State.Error, State.Error },
  107. };
  108. private int _top;
  109. private List<JsonType> _stack;
  110. private Stack _serializeStack;
  111. private TextWriter _writer;
  112. private Formatting _formatting;
  113. private char _indentChar;
  114. private int _indentation;
  115. private char _quoteChar;
  116. private bool _quoteName;
  117. private State _currentState;
  118. internal sealed class Stack
  119. {
  120. readonly ArrayList _list;
  121. public Stack (JsonWriter writer) {
  122. _list = new ArrayList ();
  123. }
  124. public void Push (object value) {
  125. _list.Add (value);
  126. }
  127. public object Pop () {
  128. int index = _list.Count - 1;
  129. object item = _list [index];
  130. _list.RemoveAt (index);
  131. return item;
  132. }
  133. public bool Contains (object item) {
  134. for (int i = 0; i < _list.Count; i++)
  135. if (item == _list [i])
  136. return true;
  137. return false;
  138. }
  139. }
  140. internal Stack SerializeStack
  141. {
  142. get
  143. {
  144. if (_serializeStack == null)
  145. _serializeStack = new Stack (this);
  146. return _serializeStack;
  147. }
  148. }
  149. /// <summary>
  150. /// Gets the state of the writer.
  151. /// </summary>
  152. public WriteState WriteState
  153. {
  154. get
  155. {
  156. switch (_currentState)
  157. {
  158. case State.Error:
  159. return WriteState.Error;
  160. case State.Closed:
  161. return WriteState.Closed;
  162. case State.Object:
  163. case State.ObjectStart:
  164. return WriteState.Object;
  165. case State.Array:
  166. case State.ArrayStart:
  167. return WriteState.Array;
  168. case State.Property:
  169. return WriteState.Property;
  170. case State.Start:
  171. return WriteState.Start;
  172. default:
  173. throw new JsonWriterException("Invalid state: " + _currentState);
  174. }
  175. }
  176. }
  177. /// <summary>
  178. /// Indicates how the output is formatted.
  179. /// </summary>
  180. public Formatting Formatting
  181. {
  182. get { return _formatting; }
  183. set { _formatting = value; }
  184. }
  185. /// <summary>
  186. /// Gets or sets how many IndentChars to write for each level in the hierarchy when <paramref name="Formatting"/> is set to <c>Formatting.Indented</c>.
  187. /// </summary>
  188. public int Indentation
  189. {
  190. get { return _indentation; }
  191. set
  192. {
  193. if (value < 0)
  194. throw new ArgumentException("Indentation value must be greater than 0.");
  195. _indentation = value;
  196. }
  197. }
  198. /// <summary>
  199. /// Gets or sets which character to use to quote attribute values.
  200. /// </summary>
  201. public char QuoteChar
  202. {
  203. get { return _quoteChar; }
  204. set
  205. {
  206. if (value != '"' && value != '\'')
  207. throw new ArgumentException(@"Invalid JavaScript string quote character. Valid quote characters are ' and "".");
  208. _quoteChar = value;
  209. }
  210. }
  211. /// <summary>
  212. /// Gets or sets which character to use for indenting when <paramref name="Formatting"/> is set to <c>Formatting.Indented</c>.
  213. /// </summary>
  214. public char IndentChar
  215. {
  216. get { return _indentChar; }
  217. set { _indentChar = value; }
  218. }
  219. /// <summary>
  220. /// Gets or sets a value indicating whether object names will be surrounded with quotes.
  221. /// </summary>
  222. public bool QuoteName
  223. {
  224. get { return _quoteName; }
  225. set { _quoteName = value; }
  226. }
  227. /// <summary>
  228. /// Creates an instance of the <c>JsonWriter</c> class using the specified <see cref="TextWriter"/>.
  229. /// </summary>
  230. /// <param name="textWriter">The <c>TextWriter</c> to write to.</param>
  231. public JsonWriter(TextWriter textWriter, int maxJsonLength)
  232. {
  233. if (textWriter == null)
  234. throw new ArgumentNullException("textWriter");
  235. if (maxJsonLength > 0)
  236. textWriter = new CountingTextWriter (textWriter, maxJsonLength);
  237. _writer = textWriter;
  238. _quoteChar = '"';
  239. _quoteName = true;
  240. _indentChar = ' ';
  241. _indentation = 2;
  242. _formatting = Formatting.None;
  243. _stack = new List<JsonType>(1);
  244. _stack.Add(JsonType.None);
  245. _currentState = State.Start;
  246. }
  247. private void Push(JsonType value)
  248. {
  249. _top++;
  250. if (_stack.Count <= _top)
  251. _stack.Add(value);
  252. else
  253. _stack[_top] = value;
  254. }
  255. private JsonType Pop()
  256. {
  257. JsonType value = Peek();
  258. _top--;
  259. return value;
  260. }
  261. private JsonType Peek()
  262. {
  263. return _stack[_top];
  264. }
  265. /// <summary>
  266. /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
  267. /// </summary>
  268. public void Flush()
  269. {
  270. _writer.Flush();
  271. }
  272. /// <summary>
  273. /// Closes this stream and the underlying stream.
  274. /// </summary>
  275. public void Close()
  276. {
  277. AutoCompleteAll();
  278. _writer.Close();
  279. }
  280. /// <summary>
  281. /// Writes the beginning of a Json object.
  282. /// </summary>
  283. public void WriteStartObject()
  284. {
  285. AutoComplete(JsonToken.StartObject);
  286. Push(JsonType.Object);
  287. _writer.Write("{");
  288. }
  289. /// <summary>
  290. /// Writes the end of a Json object.
  291. /// </summary>
  292. public void WriteEndObject()
  293. {
  294. AutoCompleteClose(JsonToken.EndObject);
  295. }
  296. /// <summary>
  297. /// Writes the beginning of a Json array.
  298. /// </summary>
  299. public void WriteStartArray()
  300. {
  301. AutoComplete(JsonToken.StartArray);
  302. Push(JsonType.Array);
  303. _writer.Write("[");
  304. }
  305. /// <summary>
  306. /// Writes the end of an array.
  307. /// </summary>
  308. public void WriteEndArray()
  309. {
  310. AutoCompleteClose(JsonToken.EndArray);
  311. }
  312. /// <summary>
  313. /// Writes the property name of a name/value pair on a Json object.
  314. /// </summary>
  315. /// <param name="name"></param>
  316. public void WritePropertyName(string name)
  317. {
  318. //_objectStack.Push(new JsonObjectInfo(JsonType.Property));
  319. AutoComplete(JsonToken.PropertyName);
  320. if (_quoteName)
  321. _writer.Write(_quoteChar);
  322. _writer.Write(name);
  323. if (_quoteName)
  324. _writer.Write(_quoteChar);
  325. _writer.Write(':');
  326. }
  327. /// <summary>
  328. /// Writes the end of the current Json object or array.
  329. /// </summary>
  330. public void WriteEnd()
  331. {
  332. WriteEnd(Peek());
  333. }
  334. private void WriteEnd(JsonType type)
  335. {
  336. switch (type)
  337. {
  338. case JsonType.Object:
  339. WriteEndObject();
  340. break;
  341. case JsonType.Array:
  342. WriteEndArray();
  343. break;
  344. default:
  345. throw new JsonWriterException("Unexpected type when writing end: " + type);
  346. }
  347. }
  348. private void AutoCompleteAll()
  349. {
  350. while (_top > 0)
  351. {
  352. WriteEnd();
  353. }
  354. }
  355. private JsonType GetTypeForCloseToken(JsonToken token)
  356. {
  357. switch (token)
  358. {
  359. case JsonToken.EndObject:
  360. return JsonType.Object;
  361. case JsonToken.EndArray:
  362. return JsonType.Array;
  363. default:
  364. throw new JsonWriterException("No type for token: " + token);
  365. }
  366. }
  367. private JsonToken GetCloseTokenForType(JsonType type)
  368. {
  369. switch (type)
  370. {
  371. case JsonType.Object:
  372. return JsonToken.EndObject;
  373. case JsonType.Array:
  374. return JsonToken.EndArray;
  375. default:
  376. throw new JsonWriterException("No close token for type: " + type);
  377. }
  378. }
  379. private void AutoCompleteClose(JsonToken tokenBeingClosed)
  380. {
  381. // write closing symbol and calculate new state
  382. int levelsToComplete = 0;
  383. for (int i = 0; i < _top; i++)
  384. {
  385. int currentLevel = _top - i;
  386. if (_stack[currentLevel] == GetTypeForCloseToken(tokenBeingClosed))
  387. {
  388. levelsToComplete = i + 1;
  389. break;
  390. }
  391. }
  392. if (levelsToComplete == 0)
  393. throw new JsonWriterException("No token to close.");
  394. for (int i = 0; i < levelsToComplete; i++)
  395. {
  396. JsonToken token = GetCloseTokenForType(Pop());
  397. if (_currentState != State.ObjectStart && _currentState != State.ArrayStart)
  398. WriteIndent();
  399. switch (token)
  400. {
  401. case JsonToken.EndObject:
  402. _writer.Write("}");
  403. break;
  404. case JsonToken.EndArray:
  405. _writer.Write("]");
  406. break;
  407. default:
  408. throw new JsonWriterException("Invalid JsonToken: " + token);
  409. }
  410. }
  411. JsonType currentLevelType = Peek();
  412. switch (currentLevelType)
  413. {
  414. case JsonType.Object:
  415. _currentState = State.Object;
  416. break;
  417. case JsonType.Array:
  418. _currentState = State.Array;
  419. break;
  420. case JsonType.None:
  421. _currentState = State.Start;
  422. break;
  423. default:
  424. throw new JsonWriterException("Unknown JsonType: " + currentLevelType);
  425. }
  426. }
  427. private void WriteIndent()
  428. {
  429. if (_formatting == Formatting.Indented)
  430. {
  431. _writer.Write(Environment.NewLine);
  432. // for each level of object...
  433. for (int i = 0; i < _top; i++)
  434. {
  435. // ...write the indent char the specified number of times
  436. for (int j = 0; j < _indentation; j++)
  437. {
  438. _writer.Write(_indentChar);
  439. }
  440. }
  441. }
  442. }
  443. private void AutoComplete(JsonToken tokenBeingWritten)
  444. {
  445. int token;
  446. switch (tokenBeingWritten)
  447. {
  448. default:
  449. token = (int) tokenBeingWritten;
  450. break;
  451. case JsonToken.Integer:
  452. case JsonToken.Float:
  453. case JsonToken.String:
  454. case JsonToken.Boolean:
  455. case JsonToken.Null:
  456. case JsonToken.Undefined:
  457. case JsonToken.Date:
  458. // a value is being written
  459. token = 5;
  460. break;
  461. }
  462. // gets new state based on the current state and what is being written
  463. State newState = stateArray[token, (int) _currentState];
  464. if (newState == State.Error)
  465. throw new JsonWriterException(string.Format("Token {0} in state {1} would result in an invalid JavaScript object.", tokenBeingWritten.ToString(), _currentState.ToString()));
  466. if ((_currentState == State.Object || _currentState == State.Array) && tokenBeingWritten != JsonToken.Comment)
  467. {
  468. _writer.Write(',');
  469. }
  470. else if (_currentState == State.Property)
  471. {
  472. if (_formatting == Formatting.Indented)
  473. _writer.Write(' ');
  474. }
  475. if (tokenBeingWritten == JsonToken.PropertyName ||
  476. (WriteState == WriteState.Array))
  477. {
  478. WriteIndent();
  479. }
  480. _currentState = newState;
  481. }
  482. private void WriteValueInternal(string value, JsonToken token)
  483. {
  484. AutoComplete(token);
  485. _writer.Write(value);
  486. }
  487. #region WriteValue methods
  488. /// <summary>
  489. /// Writes a null value.
  490. /// </summary>
  491. public void WriteNull()
  492. {
  493. WriteValueInternal(JavaScriptConvert.Null, JsonToken.Null);
  494. }
  495. /// <summary>
  496. /// Writes an undefined value.
  497. /// </summary>
  498. public void WriteUndefined()
  499. {
  500. WriteValueInternal(JavaScriptConvert.Undefined, JsonToken.Undefined);
  501. }
  502. /// <summary>
  503. /// Writes raw JavaScript manually.
  504. /// </summary>
  505. /// <param name="javaScript">The raw JavaScript to write.</param>
  506. public void WriteRaw(string javaScript)
  507. {
  508. // hack. some 'raw' or 'other' token perhaps?
  509. WriteValueInternal(javaScript, JsonToken.Undefined);
  510. }
  511. /// <summary>
  512. /// Writes a <see cref="String"/> value.
  513. /// </summary>
  514. /// <param name="value">The <see cref="String"/> value to write.</param>
  515. public void WriteValue(string value)
  516. {
  517. AutoComplete (JsonToken.String);
  518. JavaScriptConvert.WriteString(value, _quoteChar, _writer);
  519. }
  520. /// <summary>
  521. /// Writes a <see cref="Int32"/> value.
  522. /// </summary>
  523. /// <param name="value">The <see cref="Int32"/> value to write.</param>
  524. public void WriteValue(int value)
  525. {
  526. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  527. }
  528. /// <summary>
  529. /// Writes a <see cref="UInt32"/> value.
  530. /// </summary>
  531. /// <param name="value">The <see cref="UInt32"/> value to write.</param>
  532. public void WriteValue(uint value)
  533. {
  534. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  535. }
  536. /// <summary>
  537. /// Writes a <see cref="Int64"/> value.
  538. /// </summary>
  539. /// <param name="value">The <see cref="Int64"/> value to write.</param>
  540. public void WriteValue(long value)
  541. {
  542. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  543. }
  544. /// <summary>
  545. /// Writes a <see cref="UInt64"/> value.
  546. /// </summary>
  547. /// <param name="value">The <see cref="UInt64"/> value to write.</param>
  548. public void WriteValue(ulong value)
  549. {
  550. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  551. }
  552. /// <summary>
  553. /// Writes a <see cref="Single"/> value.
  554. /// </summary>
  555. /// <param name="value">The <see cref="Single"/> value to write.</param>
  556. public void WriteValue(float value)
  557. {
  558. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float);
  559. }
  560. /// <summary>
  561. /// Writes a <see cref="Double"/> value.
  562. /// </summary>
  563. /// <param name="value">The <see cref="Double"/> value to write.</param>
  564. public void WriteValue(double value)
  565. {
  566. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float);
  567. }
  568. /// <summary>
  569. /// Writes a <see cref="Boolean"/> value.
  570. /// </summary>
  571. /// <param name="value">The <see cref="Boolean"/> value to write.</param>
  572. public void WriteValue(bool value)
  573. {
  574. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Boolean);
  575. }
  576. /// <summary>
  577. /// Writes a <see cref="Int16"/> value.
  578. /// </summary>
  579. /// <param name="value">The <see cref="Int16"/> value to write.</param>
  580. public void WriteValue(short value)
  581. {
  582. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  583. }
  584. /// <summary>
  585. /// Writes a <see cref="UInt16"/> value.
  586. /// </summary>
  587. /// <param name="value">The <see cref="UInt16"/> value to write.</param>
  588. public void WriteValue(ushort value)
  589. {
  590. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  591. }
  592. /// <summary>
  593. /// Writes a <see cref="Char"/> value.
  594. /// </summary>
  595. /// <param name="value">The <see cref="Char"/> value to write.</param>
  596. public void WriteValue(char value)
  597. {
  598. AutoComplete (JsonToken.Integer);
  599. JavaScriptConvert.WriteChar (value, _writer);
  600. }
  601. /// <summary>
  602. /// Writes a <see cref="Byte"/> value.
  603. /// </summary>
  604. /// <param name="value">The <see cref="Byte"/> value to write.</param>
  605. public void WriteValue(byte value)
  606. {
  607. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  608. }
  609. /// <summary>
  610. /// Writes a <see cref="SByte"/> value.
  611. /// </summary>
  612. /// <param name="value">The <see cref="SByte"/> value to write.</param>
  613. public void WriteValue(sbyte value)
  614. {
  615. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer);
  616. }
  617. /// <summary>
  618. /// Writes a <see cref="Decimal"/> value.
  619. /// </summary>
  620. /// <param name="value">The <see cref="Decimal"/> value to write.</param>
  621. public void WriteValue(decimal value)
  622. {
  623. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float);
  624. }
  625. /// <summary>
  626. /// Writes a <see cref="DateTime"/> value.
  627. /// </summary>
  628. /// <param name="value">The <see cref="DateTime"/> value to write.</param>
  629. public void WriteValue(DateTime value)
  630. {
  631. WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date);
  632. }
  633. #endregion
  634. /// <summary>
  635. /// Writes out a comment <code>/*...*/</code> containing the specified text.
  636. /// </summary>
  637. /// <param name="text">Text to place inside the comment.</param>
  638. public void WriteComment(string text)
  639. {
  640. AutoComplete(JsonToken.Comment);
  641. _writer.Write("/*");
  642. _writer.Write(text);
  643. _writer.Write("*/");
  644. }
  645. void IDisposable.Dispose()
  646. {
  647. Dispose(true);
  648. }
  649. private void Dispose(bool disposing)
  650. {
  651. if (WriteState != WriteState.Closed)
  652. Close();
  653. }
  654. }
  655. }