123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- 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 JsonTextCursor _input;
- private readonly Stack<int> _stopChar;
- enum ReaderState { Start, BeginValue, EndValue, BeginObject, BeginArray }
- string _current;
- ReaderState _state;
- /// <summary>
- /// Constructs a JsonFormatReader to parse Json into a message
- /// </summary>
- public JsonFormatReader(string jsonText)
- {
- _input = new JsonTextCursor(jsonText.ToCharArray());
- _stopChar = new Stack<int>();
- _stopChar.Push(-1);
- _state = ReaderState.Start;
- }
- /// <summary>
- /// Constructs a JsonFormatReader to parse Json into a message
- /// </summary>
- public JsonFormatReader(TextReader input)
- {
- _input = new JsonTextCursor(input);
- _stopChar = new Stack<int>();
- _stopChar.Push(-1);
- _state = ReaderState.Start;
- }
- /// <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>
- /// Merges the contents of stream into the provided message builder
- /// </summary>
- public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
- {
- _input.Consume('{');
- _stopChar.Push('}');
- _state = ReaderState.BeginObject;
- builder.WeakMergeFrom(this, registry);
- _input.Consume((char)_stopChar.Pop());
- _state = ReaderState.EndValue;
- 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;
- JsonTextCursor.JsType type = _input.ReadVariant(out temp);
- _state = ReaderState.EndValue;
- _input.Assert(type != JsonTextCursor.JsType.Array && type != JsonTextCursor.JsType.Object, "Encountered {0} while expecting {1}", type, typeInfo);
- if (type == JsonTextCursor.JsType.Null)
- return false;
- if (type == JsonTextCursor.JsType.True) value = "1";
- else if (type == JsonTextCursor.JsType.False) value = "0";
- else value = temp as string;
- //exponent representation of integer number:
- if (value != null && type == JsonTextCursor.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;
- }
- }
- }
|