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 if (PeekChar () == '}')
73 string name = ReadStringLiteral ();
77 obj [name] = ReadCore (); // it does not reject duplicate names.
87 KeyValuePair<string, object> [] ret = new KeyValuePair<string, object>[obj.Count];
88 foreach (KeyValuePair <string, object> kvp in obj)
93 return obj.ToArray ();
103 // FIXME: what should we return?
104 return (string) null;
106 return ReadStringLiteral ();
108 if ('0' <= c && c <= '9' || c == '-')
109 return ReadNumericLiteral ();
111 throw JsonError (String.Format ("Unexpected character '{0}'", (char) c));
130 int v = has_peek ? peek : r.Read ();
150 switch (PeekChar ()) {
151 case ' ': case '\t': case '\r': case '\n':
160 // It could return either int, long or decimal, depending on the parsed value.
161 object ReadNumericLiteral ()
163 bool negative = false;
164 if (PeekChar () == '-') {
168 throw JsonError ("Invalid JSON numeric literal; extra negation");
174 bool zeroStart = PeekChar () == '0';
177 if (c < '0' || '9' < c)
179 val = val * 10 + (c - '0');
181 if (zeroStart && x == 1 && c == '0')
182 throw JsonError ("leading multiple zeros are not allowed");
187 bool hasFrac = false;
190 if (PeekChar () == '.') {
194 throw JsonError ("Invalid JSON numeric literal; extra dot");
198 if (c < '0' || '9' < c)
201 frac += (c - '0') / d;
206 throw JsonError ("Invalid JSON numeric literal; extra dot");
208 frac = Decimal.Round (frac, fdigits);
211 if (c != 'e' && c != 'E') {
213 if (negative && int.MinValue <= -val ||
214 !negative && val <= int.MaxValue)
215 return (int) (negative ? -val : val);
216 if (negative && long.MinValue <= -val ||
217 !negative && val <= long.MaxValue)
218 return (long) (negative ? -val : val);
221 return negative ? -v : v;
230 throw new ArgumentException ("Invalid JSON numeric literal; incomplete exponent");
242 throw JsonError ("Invalid JSON numeric literal; incomplete exponent");
245 if (c < '0' || '9' < c)
247 exp = exp * 10 + (c - '0');
250 // it is messy to handle exponent, so I just use Decimal.Parse() with assured JSON format.
252 return new Decimal ((double) (val + frac) / Math.Pow (10, exp));
253 int [] bits = Decimal.GetBits (val + frac);
254 return new Decimal (bits [0], bits [1], bits [2], negative, (byte) exp);
257 StringBuilder vb = new StringBuilder ();
259 string ReadStringLiteral ()
261 if (PeekChar () != '"')
262 throw JsonError ("Invalid JSON string literal format");
269 throw JsonError ("JSON string is not closed");
271 return vb.ToString ();
272 else if (c != '\\') {
273 vb.Append ((char) c);
277 // escaped expression
280 throw JsonError ("Invalid JSON string literal; incomplete escape sequence");
285 vb.Append ((char) c);
304 for (int i = 0; i < 4; i++) {
306 if ((c = ReadChar ()) < 0)
307 throw JsonError ("Incomplete unicode character escape literal");
308 if ('0' <= c && c <= '9')
309 cp += (ushort) (c - '0');
310 if ('A' <= c && c <= 'F')
311 cp += (ushort) (c - 'A' + 10);
312 if ('a' <= c && c <= 'f')
313 cp += (ushort) (c - 'a' + 10);
315 vb.Append ((char) cp);
318 throw JsonError ("Invalid JSON string literal; unexpected escape character");
323 void Expect (char expected)
326 if ((c = ReadChar ()) != expected)
327 throw JsonError (String.Format ("Expected '{0}', got '{1}'", expected, (char) c));
330 void Expect (string expected)
332 for (int i = 0; i < expected.Length; i++)
333 if (ReadChar () != expected [i])
334 throw JsonError (String.Format ("Expected '{0}', differed at {1}", expected, i));
337 Exception JsonError (string msg)
339 return new ArgumentException (String.Format ("{0}. At line {1}, column {2}", msg, line, column));