PageRenderTime 79ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/managed/Json131/Src/Newtonsoft.Json/JsonWriter.cs

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