JsonFormatReader.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Xml;
  5. namespace Google.ProtocolBuffers.Serialization
  6. {
  7. /// <summary>
  8. /// JsonFormatReader is used to parse Json into a message or an array of messages
  9. /// </summary>
  10. public class JsonFormatReader : AbstractTextReader
  11. {
  12. private readonly JsonCursor _input;
  13. // The expected token that ends the current item, either ']' or '}'
  14. private readonly Stack<int> _stopChar;
  15. private enum ReaderState
  16. {
  17. Start,
  18. BeginValue,
  19. EndValue,
  20. BeginObject,
  21. BeginArray
  22. }
  23. private string _current;
  24. private ReaderState _state;
  25. /// <summary>
  26. /// Constructs a JsonFormatReader to parse Json into a message, this method does not use text encoding, all bytes MUST
  27. /// represent ASCII character values.
  28. /// </summary>
  29. public static JsonFormatReader CreateInstance(Stream stream)
  30. {
  31. return new JsonFormatReader(JsonCursor.CreateInstance(stream));
  32. }
  33. /// <summary>
  34. /// Constructs a JsonFormatReader to parse Json into a message, this method does not use text encoding, all bytes MUST
  35. /// represent ASCII character values.
  36. /// </summary>
  37. public static JsonFormatReader CreateInstance(byte[] bytes)
  38. {
  39. return new JsonFormatReader(JsonCursor.CreateInstance(bytes));
  40. }
  41. /// <summary>
  42. /// Constructs a JsonFormatReader to parse Json into a message
  43. /// </summary>
  44. public static JsonFormatReader CreateInstance(string jsonText)
  45. {
  46. return new JsonFormatReader(JsonCursor.CreateInstance(jsonText));
  47. }
  48. /// <summary>
  49. /// Constructs a JsonFormatReader to parse Json into a message
  50. /// </summary>
  51. public static JsonFormatReader CreateInstance(TextReader input)
  52. {
  53. return new JsonFormatReader(JsonCursor.CreateInstance(input));
  54. }
  55. /// <summary>
  56. /// Constructs a JsonFormatReader to parse Json into a message
  57. /// </summary>
  58. internal JsonFormatReader(JsonCursor input)
  59. {
  60. _input = input;
  61. _stopChar = new Stack<int>();
  62. _stopChar.Push(-1);
  63. _state = ReaderState.Start;
  64. }
  65. /// <summary>
  66. /// Constructs a JsonFormatReader to parse Json into a message
  67. /// </summary>
  68. protected JsonFormatReader(TextReader input)
  69. : this(JsonCursor.CreateInstance(input))
  70. {
  71. }
  72. /// <summary>
  73. /// Returns true if the reader is currently on an array element
  74. /// </summary>
  75. public bool IsArrayMessage
  76. {
  77. get { return _input.NextChar == '['; }
  78. }
  79. /// <summary>
  80. /// Returns an enumerator that is used to cursor over an array of messages
  81. /// </summary>
  82. /// <remarks>
  83. /// This is generally used when receiving an array of messages rather than a single root message
  84. /// </remarks>
  85. public IEnumerable<JsonFormatReader> EnumerateArray()
  86. {
  87. foreach (string ignored in ForeachArrayItem(_current))
  88. {
  89. yield return this;
  90. }
  91. }
  92. /// <summary>
  93. /// Reads the root-message preamble specific to this formatter
  94. /// </summary>
  95. public override void ReadMessageStart()
  96. {
  97. _input.Consume('{');
  98. _stopChar.Push('}');
  99. _state = ReaderState.BeginObject;
  100. }
  101. /// <summary>
  102. /// Reads the root-message close specific to this formatter
  103. /// </summary>
  104. public override void ReadMessageEnd()
  105. {
  106. _input.Consume((char)_stopChar.Pop());
  107. _state = ReaderState.EndValue;
  108. }
  109. /// <summary>
  110. /// Merges the contents of stream into the provided message builder
  111. /// </summary>
  112. public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
  113. {
  114. ReadMessageStart();
  115. builder.WeakMergeFrom(this, registry);
  116. ReadMessageEnd();
  117. return builder;
  118. }
  119. /// <summary>
  120. /// Causes the reader to skip past this field
  121. /// </summary>
  122. protected override void Skip()
  123. {
  124. object temp;
  125. _input.ReadVariant(out temp);
  126. _state = ReaderState.EndValue;
  127. }
  128. /// <summary>
  129. /// Peeks at the next field in the input stream and returns what information is available.
  130. /// </summary>
  131. /// <remarks>
  132. /// This may be called multiple times without actually reading the field. Only after the field
  133. /// is either read, or skipped, should PeekNext return a different value.
  134. /// </remarks>
  135. protected override bool PeekNext(out string field)
  136. {
  137. field = _current;
  138. if (_state == ReaderState.BeginValue)
  139. {
  140. return true;
  141. }
  142. int next = _input.NextChar;
  143. if (next == _stopChar.Peek())
  144. {
  145. return false;
  146. }
  147. _input.Assert(next != -1, "Unexpected end of file.");
  148. //not sure about this yet, it will allow {, "a":true }
  149. if (_state == ReaderState.EndValue && !_input.TryConsume(','))
  150. {
  151. return false;
  152. }
  153. field = _current = _input.ReadString();
  154. _input.Consume(':');
  155. _state = ReaderState.BeginValue;
  156. return true;
  157. }
  158. /// <summary>
  159. /// Returns true if it was able to read a String from the input
  160. /// </summary>
  161. protected override bool ReadAsText(ref string value, Type typeInfo)
  162. {
  163. object temp;
  164. JsonCursor.JsType type = _input.ReadVariant(out temp);
  165. _state = ReaderState.EndValue;
  166. _input.Assert(type != JsonCursor.JsType.Array && type != JsonCursor.JsType.Object,
  167. "Encountered {0} while expecting {1}", type, typeInfo);
  168. if (type == JsonCursor.JsType.Null)
  169. {
  170. return false;
  171. }
  172. if (type == JsonCursor.JsType.True)
  173. {
  174. value = "1";
  175. }
  176. else if (type == JsonCursor.JsType.False)
  177. {
  178. value = "0";
  179. }
  180. else
  181. {
  182. value = temp as string;
  183. }
  184. //exponent representation of integer number:
  185. if (value != null && type == JsonCursor.JsType.Number &&
  186. (typeInfo != typeof(double) && typeInfo != typeof(float)) &&
  187. value.IndexOf("e", StringComparison.OrdinalIgnoreCase) > 0)
  188. {
  189. value = XmlConvert.ToString((long) Math.Round(XmlConvert.ToDouble(value), 0));
  190. }
  191. return value != null;
  192. }
  193. /// <summary>
  194. /// Returns true if it was able to read a ByteString from the input
  195. /// </summary>
  196. protected override bool Read(ref ByteString value)
  197. {
  198. string bytes = null;
  199. if (Read(ref bytes))
  200. {
  201. value = ByteString.FromBase64(bytes);
  202. return true;
  203. }
  204. return false;
  205. }
  206. /// <summary>
  207. /// Cursors through the array elements and stops at the end of the array
  208. /// </summary>
  209. protected override IEnumerable<string> ForeachArrayItem(string field)
  210. {
  211. _input.Consume('[');
  212. _stopChar.Push(']');
  213. _state = ReaderState.BeginArray;
  214. while (_input.NextChar != ']')
  215. {
  216. _current = field;
  217. yield return field;
  218. if (!_input.TryConsume(','))
  219. {
  220. break;
  221. }
  222. }
  223. _input.Consume((char) _stopChar.Pop());
  224. _state = ReaderState.EndValue;
  225. }
  226. /// <summary>
  227. /// Merges the input stream into the provided IBuilderLite
  228. /// </summary>
  229. protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
  230. {
  231. Merge(builder, registry);
  232. return true;
  233. }
  234. }
  235. }