JsonFormatReader.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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 JsonTextCursor _input;
  13. private readonly Stack<int> _stopChar;
  14. enum ReaderState { Start, BeginValue, EndValue, BeginObject, BeginArray }
  15. string _current;
  16. ReaderState _state;
  17. /// <summary>
  18. /// Constructs a JsonFormatReader to parse Json into a message
  19. /// </summary>
  20. public JsonFormatReader(string jsonText)
  21. {
  22. _input = new JsonTextCursor(jsonText.ToCharArray());
  23. _stopChar = new Stack<int>();
  24. _stopChar.Push(-1);
  25. _state = ReaderState.Start;
  26. }
  27. /// <summary>
  28. /// Constructs a JsonFormatReader to parse Json into a message
  29. /// </summary>
  30. public JsonFormatReader(TextReader input)
  31. {
  32. _input = new JsonTextCursor(input);
  33. _stopChar = new Stack<int>();
  34. _stopChar.Push(-1);
  35. _state = ReaderState.Start;
  36. }
  37. /// <summary>
  38. /// Returns true if the reader is currently on an array element
  39. /// </summary>
  40. public bool IsArrayMessage { get { return _input.NextChar == '['; } }
  41. /// <summary>
  42. /// Returns an enumerator that is used to cursor over an array of messages
  43. /// </summary>
  44. /// <remarks>
  45. /// This is generally used when receiving an array of messages rather than a single root message
  46. /// </remarks>
  47. public IEnumerable<JsonFormatReader> EnumerateArray()
  48. {
  49. foreach (string ignored in ForeachArrayItem(_current))
  50. yield return this;
  51. }
  52. /// <summary>
  53. /// Merges the contents of stream into the provided message builder
  54. /// </summary>
  55. public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
  56. {
  57. _input.Consume('{');
  58. _stopChar.Push('}');
  59. _state = ReaderState.BeginObject;
  60. builder.WeakMergeFrom(this, registry);
  61. _input.Consume((char)_stopChar.Pop());
  62. _state = ReaderState.EndValue;
  63. return builder;
  64. }
  65. /// <summary>
  66. /// Causes the reader to skip past this field
  67. /// </summary>
  68. protected override void Skip()
  69. {
  70. object temp;
  71. _input.ReadVariant(out temp);
  72. _state = ReaderState.EndValue;
  73. }
  74. /// <summary>
  75. /// Peeks at the next field in the input stream and returns what information is available.
  76. /// </summary>
  77. /// <remarks>
  78. /// This may be called multiple times without actually reading the field. Only after the field
  79. /// is either read, or skipped, should PeekNext return a different value.
  80. /// </remarks>
  81. protected override bool PeekNext(out string field)
  82. {
  83. field = _current;
  84. if(_state == ReaderState.BeginValue)
  85. return true;
  86. int next = _input.NextChar;
  87. if (next == _stopChar.Peek())
  88. return false;
  89. _input.Assert(next != -1, "Unexpected end of file.");
  90. //not sure about this yet, it will allow {, "a":true }
  91. if (_state == ReaderState.EndValue && !_input.TryConsume(','))
  92. return false;
  93. field = _current = _input.ReadString();
  94. _input.Consume(':');
  95. _state = ReaderState.BeginValue;
  96. return true;
  97. }
  98. /// <summary>
  99. /// Returns true if it was able to read a String from the input
  100. /// </summary>
  101. protected override bool ReadAsText(ref string value, Type typeInfo)
  102. {
  103. object temp;
  104. JsonTextCursor.JsType type = _input.ReadVariant(out temp);
  105. _state = ReaderState.EndValue;
  106. _input.Assert(type != JsonTextCursor.JsType.Array && type != JsonTextCursor.JsType.Object, "Encountered {0} while expecting {1}", type, typeInfo);
  107. if (type == JsonTextCursor.JsType.Null)
  108. return false;
  109. if (type == JsonTextCursor.JsType.True) value = "1";
  110. else if (type == JsonTextCursor.JsType.False) value = "0";
  111. else value = temp as string;
  112. //exponent representation of integer number:
  113. if (value != null && type == JsonTextCursor.JsType.Number &&
  114. (typeInfo != typeof(double) && typeInfo != typeof(float)) &&
  115. value.IndexOf("e", StringComparison.OrdinalIgnoreCase) > 0)
  116. {
  117. value = XmlConvert.ToString((long)Math.Round(XmlConvert.ToDouble(value), 0));
  118. }
  119. return value != null;
  120. }
  121. /// <summary>
  122. /// Returns true if it was able to read a ByteString from the input
  123. /// </summary>
  124. protected override bool Read(ref ByteString value)
  125. {
  126. string bytes = null;
  127. if (Read(ref bytes))
  128. {
  129. value = ByteString.FromBase64(bytes);
  130. return true;
  131. }
  132. return false;
  133. }
  134. /// <summary>
  135. /// Cursors through the array elements and stops at the end of the array
  136. /// </summary>
  137. protected override IEnumerable<string> ForeachArrayItem(string field)
  138. {
  139. _input.Consume('[');
  140. _stopChar.Push(']');
  141. _state = ReaderState.BeginArray;
  142. while (_input.NextChar != ']')
  143. {
  144. _current = field;
  145. yield return field;
  146. if(!_input.TryConsume(','))
  147. break;
  148. }
  149. _input.Consume((char)_stopChar.Pop());
  150. _state = ReaderState.EndValue;
  151. }
  152. /// <summary>
  153. /// Merges the input stream into the provided IBuilderLite
  154. /// </summary>
  155. protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
  156. {
  157. Merge(builder, registry);
  158. return true;
  159. }
  160. }
  161. }