JsonWriter.cs 18 KB

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