2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Globalization;
9 namespace System.Runtime.Serialization.Json
11 internal class JavaScriptReader
14 int line = 1, column = 0;
15 bool raise_on_number_error; // FIXME: use it
17 public JavaScriptReader (TextReader reader, bool raiseOnNumberError)
20 throw new ArgumentNullException ("reader");
22 raise_on_number_error = raiseOnNumberError;
27 object v = ReadCore ();
30 throw JsonError (String.Format ("extra characters in JSON input"));
39 throw JsonError ("Incomplete JSON input");
43 var list = new List<object> ();
45 if (PeekChar () == ']') {
50 list.Add (ReadCore ());
58 if (ReadChar () != ']')
59 throw JsonError ("JSON array must end with ']'");
60 return list.ToArray ();
63 var obj = new Dictionary<string,object> ();
65 if (PeekChar () == '}') {
71 string name = ReadStringLiteral ();
75 obj [name] = ReadCore (); // it does not reject duplicate names.
83 return obj.ToArray ();
92 // FIXME: what should we return?
95 return ReadStringLiteral ();
97 if ('0' <= c && c <= '9' || c == '-')
98 return ReadNumericLiteral ();
100 throw JsonError (String.Format ("Unexpected character '{0}'", (char) c));
119 int v = has_peek ? peek : r.Read ();
139 switch (PeekChar ()) {
140 case ' ': case '\t': case '\r': case '\n':
149 // It could return either int, long or decimal, depending on the parsed value.
150 object ReadNumericLiteral ()
152 bool negative = false;
153 if (PeekChar () == '-') {
157 throw JsonError ("Invalid JSON numeric literal; extra negation");
163 bool zeroStart = PeekChar () == '0';
166 if (c < '0' || '9' < c)
168 val = val * 10 + (c - '0');
170 if (zeroStart && x == 1 && c == '0')
171 throw JsonError ("leading multiple zeros are not allowed");
176 bool hasFrac = false;
179 if (PeekChar () == '.') {
183 throw JsonError ("Invalid JSON numeric literal; extra dot");
187 if (c < '0' || '9' < c)
190 frac += (c - '0') / d;
195 throw JsonError ("Invalid JSON numeric literal; extra dot");
197 frac = Decimal.Round (frac, fdigits);
200 if (c != 'e' && c != 'E') {
202 if (negative && int.MinValue <= -val ||
203 !negative && val <= int.MaxValue)
204 return (int) (negative ? -val : val);
205 if (negative && long.MinValue <= -val ||
206 !negative && val <= long.MaxValue)
207 return (long) (negative ? -val : val);
210 return negative ? -v : v;
219 throw new ArgumentException ("Invalid JSON numeric literal; incomplete exponent");
230 throw JsonError ("Invalid JSON numeric literal; incomplete exponent");
233 if (c < '0' || '9' < c)
235 exp = exp * 10 + (c - '0');
238 // it is messy to handle exponent, so I just use Decimal.Parse() with assured JSON format.
239 int [] bits = Decimal.GetBits (val + frac);
240 return new Decimal (bits [0], bits [1], bits [2], negative, (byte) exp);
243 StringBuilder vb = new StringBuilder ();
245 string ReadStringLiteral ()
247 if (PeekChar () != '"')
248 throw JsonError ("Invalid JSON string literal format");
255 throw JsonError ("JSON string is not closed");
257 return vb.ToString ();
258 else if (c != '\\') {
259 vb.Append ((char) c);
263 // escaped expression
266 throw JsonError ("Invalid JSON string literal; incomplete escape sequence");
271 vb.Append ((char) c);
290 for (int i = 0; i < 4; i++) {
292 if ((c = ReadChar ()) < 0)
293 throw JsonError ("Incomplete unicode character escape literal");
294 if ('0' <= c && c <= '9')
295 cp += (ushort) (c - '0');
296 if ('A' <= c && c <= 'F')
297 cp += (ushort) (c - 'A' + 10);
298 if ('a' <= c && c <= 'f')
299 cp += (ushort) (c - 'a' + 10);
301 vb.Append ((char) cp);
304 throw JsonError ("Invalid JSON string literal; unexpected escape character");
309 void Expect (char expected)
312 if ((c = ReadChar ()) != expected)
313 throw JsonError (String.Format ("Expected '{0}', got '{1}'", expected, (char) c));
316 void Expect (string expected)
319 for (int i = 0; i < expected.Length; i++)
320 if ((c = ReadChar ()) != expected [i])
321 throw JsonError (String.Format ("Expected '{0}', differed at {1}", expected, i));
324 Exception JsonError (string msg)
326 return new ArgumentException (String.Format ("{0}. At line {1}, column {2}", msg, line, column));