| 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;        }    }}
 |