blob: d4505d709d35af4d1eb715db79d5dd746784dcab [file] [log] [blame]
csharptestafe844b2011-06-10 16:03:22 -05001using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Xml;
5
6namespace Google.ProtocolBuffers.Serialization
7{
8 /// <summary>
9 /// JsonFormatReader is used to parse Json into a message or an array of messages
10 /// </summary>
11 public class JsonFormatReader : AbstractTextReader
12 {
csharptest7fc785c2011-06-10 23:54:53 -050013 private readonly JsonCursor _input;
csharptestafe844b2011-06-10 16:03:22 -050014 private readonly Stack<int> _stopChar;
15
csharptest74c5e0c2011-07-14 13:06:22 -050016 private enum ReaderState
17 {
18 Start,
19 BeginValue,
20 EndValue,
21 BeginObject,
22 BeginArray
23 }
24
25 private string _current;
26 private ReaderState _state;
csharptestafe844b2011-06-10 16:03:22 -050027
28 /// <summary>
csharptest7fc785c2011-06-10 23:54:53 -050029 /// Constructs a JsonFormatReader to parse Json into a message, this method does not use text encoding, all bytes MUST
30 /// represent ASCII character values.
csharptestafe844b2011-06-10 16:03:22 -050031 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050032 public static JsonFormatReader CreateInstance(Stream stream)
33 {
34 return new JsonFormatReader(JsonCursor.CreateInstance(stream));
35 }
36
csharptest7fc785c2011-06-10 23:54:53 -050037 /// <summary>
38 /// Constructs a JsonFormatReader to parse Json into a message, this method does not use text encoding, all bytes MUST
39 /// represent ASCII character values.
40 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050041 public static JsonFormatReader CreateInstance(byte[] bytes)
42 {
43 return new JsonFormatReader(JsonCursor.CreateInstance(bytes));
44 }
45
csharptestafe844b2011-06-10 16:03:22 -050046 /// <summary>
47 /// Constructs a JsonFormatReader to parse Json into a message
48 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050049 public static JsonFormatReader CreateInstance(string jsonText)
50 {
51 return new JsonFormatReader(JsonCursor.CreateInstance(jsonText));
52 }
53
csharptest7fc785c2011-06-10 23:54:53 -050054 /// <summary>
55 /// Constructs a JsonFormatReader to parse Json into a message
56 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050057 public static JsonFormatReader CreateInstance(TextReader input)
58 {
59 return new JsonFormatReader(JsonCursor.CreateInstance(input));
60 }
csharptest7fc785c2011-06-10 23:54:53 -050061
62 /// <summary>
63 /// Constructs a JsonFormatReader to parse Json into a message
64 /// </summary>
65 internal JsonFormatReader(JsonCursor input)
csharptestafe844b2011-06-10 16:03:22 -050066 {
csharptest7fc785c2011-06-10 23:54:53 -050067 _input = input;
csharptestafe844b2011-06-10 16:03:22 -050068 _stopChar = new Stack<int>();
69 _stopChar.Push(-1);
70 _state = ReaderState.Start;
71 }
72
73 /// <summary>
csharptest7fc785c2011-06-10 23:54:53 -050074 /// Constructs a JsonFormatReader to parse Json into a message
75 /// </summary>
76 protected JsonFormatReader(TextReader input)
77 : this(JsonCursor.CreateInstance(input))
csharptest74c5e0c2011-07-14 13:06:22 -050078 {
79 }
csharptest7fc785c2011-06-10 23:54:53 -050080
81 /// <summary>
csharptestafe844b2011-06-10 16:03:22 -050082 /// Returns true if the reader is currently on an array element
83 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050084 public bool IsArrayMessage
85 {
86 get { return _input.NextChar == '['; }
87 }
csharptestafe844b2011-06-10 16:03:22 -050088
89 /// <summary>
90 /// Returns an enumerator that is used to cursor over an array of messages
91 /// </summary>
92 /// <remarks>
93 /// This is generally used when receiving an array of messages rather than a single root message
94 /// </remarks>
95 public IEnumerable<JsonFormatReader> EnumerateArray()
96 {
97 foreach (string ignored in ForeachArrayItem(_current))
csharptest74c5e0c2011-07-14 13:06:22 -050098 {
csharptestafe844b2011-06-10 16:03:22 -050099 yield return this;
csharptest74c5e0c2011-07-14 13:06:22 -0500100 }
csharptestafe844b2011-06-10 16:03:22 -0500101 }
102
103 /// <summary>
104 /// Merges the contents of stream into the provided message builder
105 /// </summary>
106 public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
107 {
108 _input.Consume('{');
109 _stopChar.Push('}');
110
111 _state = ReaderState.BeginObject;
112 builder.WeakMergeFrom(this, registry);
csharptest74c5e0c2011-07-14 13:06:22 -0500113 _input.Consume((char) _stopChar.Pop());
csharptestafe844b2011-06-10 16:03:22 -0500114 _state = ReaderState.EndValue;
115 return builder;
116 }
117
118 /// <summary>
119 /// Causes the reader to skip past this field
120 /// </summary>
121 protected override void Skip()
122 {
123 object temp;
124 _input.ReadVariant(out temp);
125 _state = ReaderState.EndValue;
126 }
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;
csharptest74c5e0c2011-07-14 13:06:22 -0500138 if (_state == ReaderState.BeginValue)
139 {
csharptestafe844b2011-06-10 16:03:22 -0500140 return true;
csharptest74c5e0c2011-07-14 13:06:22 -0500141 }
csharptestafe844b2011-06-10 16:03:22 -0500142
143 int next = _input.NextChar;
144 if (next == _stopChar.Peek())
csharptest74c5e0c2011-07-14 13:06:22 -0500145 {
csharptestafe844b2011-06-10 16:03:22 -0500146 return false;
csharptest74c5e0c2011-07-14 13:06:22 -0500147 }
csharptestafe844b2011-06-10 16:03:22 -0500148
149 _input.Assert(next != -1, "Unexpected end of file.");
150
151 //not sure about this yet, it will allow {, "a":true }
152 if (_state == ReaderState.EndValue && !_input.TryConsume(','))
csharptest74c5e0c2011-07-14 13:06:22 -0500153 {
csharptestafe844b2011-06-10 16:03:22 -0500154 return false;
csharptest74c5e0c2011-07-14 13:06:22 -0500155 }
csharptestafe844b2011-06-10 16:03:22 -0500156
157 field = _current = _input.ReadString();
158 _input.Consume(':');
159 _state = ReaderState.BeginValue;
160 return true;
161 }
162
163 /// <summary>
164 /// Returns true if it was able to read a String from the input
165 /// </summary>
166 protected override bool ReadAsText(ref string value, Type typeInfo)
167 {
168 object temp;
csharptest7fc785c2011-06-10 23:54:53 -0500169 JsonCursor.JsType type = _input.ReadVariant(out temp);
csharptestafe844b2011-06-10 16:03:22 -0500170 _state = ReaderState.EndValue;
171
csharptest74c5e0c2011-07-14 13:06:22 -0500172 _input.Assert(type != JsonCursor.JsType.Array && type != JsonCursor.JsType.Object,
173 "Encountered {0} while expecting {1}", type, typeInfo);
csharptest7fc785c2011-06-10 23:54:53 -0500174 if (type == JsonCursor.JsType.Null)
csharptest74c5e0c2011-07-14 13:06:22 -0500175 {
csharptestafe844b2011-06-10 16:03:22 -0500176 return false;
csharptest74c5e0c2011-07-14 13:06:22 -0500177 }
178 if (type == JsonCursor.JsType.True)
179 {
180 value = "1";
181 }
182 else if (type == JsonCursor.JsType.False)
183 {
184 value = "0";
185 }
186 else
187 {
188 value = temp as string;
189 }
csharptestafe844b2011-06-10 16:03:22 -0500190
191 //exponent representation of integer number:
csharptest7fc785c2011-06-10 23:54:53 -0500192 if (value != null && type == JsonCursor.JsType.Number &&
csharptest74c5e0c2011-07-14 13:06:22 -0500193 (typeInfo != typeof (double) && typeInfo != typeof (float)) &&
csharptestafe844b2011-06-10 16:03:22 -0500194 value.IndexOf("e", StringComparison.OrdinalIgnoreCase) > 0)
195 {
csharptest74c5e0c2011-07-14 13:06:22 -0500196 value = XmlConvert.ToString((long) Math.Round(XmlConvert.ToDouble(value), 0));
csharptestafe844b2011-06-10 16:03:22 -0500197 }
198 return value != null;
199 }
200
201 /// <summary>
202 /// Returns true if it was able to read a ByteString from the input
203 /// </summary>
204 protected override bool Read(ref ByteString value)
205 {
206 string bytes = null;
207 if (Read(ref bytes))
208 {
209 value = ByteString.FromBase64(bytes);
210 return true;
211 }
212 return false;
213 }
214
215 /// <summary>
216 /// Cursors through the array elements and stops at the end of the array
217 /// </summary>
218 protected override IEnumerable<string> ForeachArrayItem(string field)
219 {
220 _input.Consume('[');
221 _stopChar.Push(']');
222 _state = ReaderState.BeginArray;
223 while (_input.NextChar != ']')
224 {
225 _current = field;
226 yield return field;
csharptest74c5e0c2011-07-14 13:06:22 -0500227 if (!_input.TryConsume(','))
228 {
csharptestafe844b2011-06-10 16:03:22 -0500229 break;
csharptest74c5e0c2011-07-14 13:06:22 -0500230 }
csharptestafe844b2011-06-10 16:03:22 -0500231 }
csharptest74c5e0c2011-07-14 13:06:22 -0500232 _input.Consume((char) _stopChar.Pop());
csharptestafe844b2011-06-10 16:03:22 -0500233 _state = ReaderState.EndValue;
234 }
235
236 /// <summary>
237 /// Merges the input stream into the provided IBuilderLite
238 /// </summary>
239 protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
240 {
241 Merge(builder, registry);
242 return true;
243 }
csharptestafe844b2011-06-10 16:03:22 -0500244 }
csharptest74c5e0c2011-07-14 13:06:22 -0500245}