aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/ProtocolBuffers.Serialization/JsonFormatReader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'csharp/src/ProtocolBuffers.Serialization/JsonFormatReader.cs')
-rw-r--r--csharp/src/ProtocolBuffers.Serialization/JsonFormatReader.cs262
1 files changed, 262 insertions, 0 deletions
diff --git a/csharp/src/ProtocolBuffers.Serialization/JsonFormatReader.cs b/csharp/src/ProtocolBuffers.Serialization/JsonFormatReader.cs
new file mode 100644
index 00000000..423196d8
--- /dev/null
+++ b/csharp/src/ProtocolBuffers.Serialization/JsonFormatReader.cs
@@ -0,0 +1,262 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// JsonFormatReader is used to parse Json into a message or an array of messages
+ /// </summary>
+ public class JsonFormatReader : AbstractTextReader
+ {
+ private readonly JsonCursor _input;
+ // The expected token that ends the current item, either ']' or '}'
+ private readonly Stack<int> _stopChar;
+
+ private enum ReaderState
+ {
+ Start,
+ BeginValue,
+ EndValue,
+ BeginObject,
+ BeginArray
+ }
+
+ private string _current;
+ private ReaderState _state;
+
+ /// <summary>
+ /// Constructs a JsonFormatReader to parse Json into a message, this method does not use text encoding, all bytes MUST
+ /// represent ASCII character values.
+ /// </summary>
+ public static JsonFormatReader CreateInstance(Stream stream)
+ {
+ return new JsonFormatReader(JsonCursor.CreateInstance(stream));
+ }
+
+ /// <summary>
+ /// Constructs a JsonFormatReader to parse Json into a message, this method does not use text encoding, all bytes MUST
+ /// represent ASCII character values.
+ /// </summary>
+ public static JsonFormatReader CreateInstance(byte[] bytes)
+ {
+ return new JsonFormatReader(JsonCursor.CreateInstance(bytes));
+ }
+
+ /// <summary>
+ /// Constructs a JsonFormatReader to parse Json into a message
+ /// </summary>
+ public static JsonFormatReader CreateInstance(string jsonText)
+ {
+ return new JsonFormatReader(JsonCursor.CreateInstance(jsonText));
+ }
+
+ /// <summary>
+ /// Constructs a JsonFormatReader to parse Json into a message
+ /// </summary>
+ public static JsonFormatReader CreateInstance(TextReader input)
+ {
+ return new JsonFormatReader(JsonCursor.CreateInstance(input));
+ }
+
+ /// <summary>
+ /// Constructs a JsonFormatReader to parse Json into a message
+ /// </summary>
+ internal JsonFormatReader(JsonCursor input)
+ {
+ _input = input;
+ _stopChar = new Stack<int>();
+ _stopChar.Push(-1);
+ _state = ReaderState.Start;
+ }
+
+ /// <summary>
+ /// Constructs a JsonFormatReader to parse Json into a message
+ /// </summary>
+ protected JsonFormatReader(TextReader input)
+ : this(JsonCursor.CreateInstance(input))
+ {
+ }
+
+ /// <summary>
+ /// Returns true if the reader is currently on an array element
+ /// </summary>
+ public bool IsArrayMessage
+ {
+ get { return _input.NextChar == '['; }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that is used to cursor over an array of messages
+ /// </summary>
+ /// <remarks>
+ /// This is generally used when receiving an array of messages rather than a single root message
+ /// </remarks>
+ public IEnumerable<JsonFormatReader> EnumerateArray()
+ {
+ foreach (string ignored in ForeachArrayItem(_current))
+ {
+ yield return this;
+ }
+ }
+
+ /// <summary>
+ /// Reads the root-message preamble specific to this formatter
+ /// </summary>
+ public override void ReadMessageStart()
+ {
+ _input.Consume('{');
+ _stopChar.Push('}');
+
+ _state = ReaderState.BeginObject;
+ }
+
+ /// <summary>
+ /// Reads the root-message close specific to this formatter
+ /// </summary>
+ public override void ReadMessageEnd()
+ {
+ _input.Consume((char)_stopChar.Pop());
+ _state = ReaderState.EndValue;
+ }
+
+ /// <summary>
+ /// Merges the contents of stream into the provided message builder
+ /// </summary>
+ public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
+ {
+ ReadMessageStart();
+ builder.WeakMergeFrom(this, registry);
+ ReadMessageEnd();
+ return builder;
+ }
+
+ /// <summary>
+ /// Causes the reader to skip past this field
+ /// </summary>
+ protected override void Skip()
+ {
+ object temp;
+ _input.ReadVariant(out temp);
+ _state = ReaderState.EndValue;
+ }
+
+ /// <summary>
+ /// Peeks at the next field in the input stream and returns what information is available.
+ /// </summary>
+ /// <remarks>
+ /// This may be called multiple times without actually reading the field. Only after the field
+ /// is either read, or skipped, should PeekNext return a different value.
+ /// </remarks>
+ protected override bool PeekNext(out string field)
+ {
+ field = _current;
+ if (_state == ReaderState.BeginValue)
+ {
+ return true;
+ }
+
+ int next = _input.NextChar;
+ if (next == _stopChar.Peek())
+ {
+ return false;
+ }
+
+ _input.Assert(next != -1, "Unexpected end of file.");
+
+ //not sure about this yet, it will allow {, "a":true }
+ if (_state == ReaderState.EndValue && !_input.TryConsume(','))
+ {
+ return false;
+ }
+
+ field = _current = _input.ReadString();
+ _input.Consume(':');
+ _state = ReaderState.BeginValue;
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a String from the input
+ /// </summary>
+ protected override bool ReadAsText(ref string value, Type typeInfo)
+ {
+ object temp;
+ JsonCursor.JsType type = _input.ReadVariant(out temp);
+ _state = ReaderState.EndValue;
+
+ _input.Assert(type != JsonCursor.JsType.Array && type != JsonCursor.JsType.Object,
+ "Encountered {0} while expecting {1}", type, typeInfo);
+ if (type == JsonCursor.JsType.Null)
+ {
+ return false;
+ }
+ if (type == JsonCursor.JsType.True)
+ {
+ value = "1";
+ }
+ else if (type == JsonCursor.JsType.False)
+ {
+ value = "0";
+ }
+ else
+ {
+ value = temp as string;
+ }
+
+ //exponent representation of integer number:
+ if (value != null && type == JsonCursor.JsType.Number &&
+ (typeInfo != typeof(double) && typeInfo != typeof(float)) &&
+ value.IndexOf("e", StringComparison.OrdinalIgnoreCase) > 0)
+ {
+ value = XmlConvert.ToString((long) Math.Round(XmlConvert.ToDouble(value), 0));
+ }
+ return value != null;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a ByteString from the input
+ /// </summary>
+ protected override bool Read(ref ByteString value)
+ {
+ string bytes = null;
+ if (Read(ref bytes))
+ {
+ value = ByteString.FromBase64(bytes);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Cursors through the array elements and stops at the end of the array
+ /// </summary>
+ protected override IEnumerable<string> ForeachArrayItem(string field)
+ {
+ _input.Consume('[');
+ _stopChar.Push(']');
+ _state = ReaderState.BeginArray;
+ while (_input.NextChar != ']')
+ {
+ _current = field;
+ yield return field;
+ if (!_input.TryConsume(','))
+ {
+ break;
+ }
+ }
+ _input.Consume((char) _stopChar.Pop());
+ _state = ReaderState.EndValue;
+ }
+
+ /// <summary>
+ /// Merges the input stream into the provided IBuilderLite
+ /// </summary>
+ protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
+ {
+ Merge(builder, registry);
+ return true;
+ }
+ }
+} \ No newline at end of file