5 // Marek Habersack <grendel@twistedcode.net>
7 // (C) 2008 Novell, Inc. http://novell.com/
8 // Copyright 2011, Xamarin, Inc (http://xamarin.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 // Code is based on JSON_checker (http://www.json.org/JSON_checker/) and JSON_parser
30 // (http://fara.cs.uni-potsdam.de/~jsg/json_parser/) C sources.
33 using System.Collections;
34 using System.Collections.Generic;
36 using System.Globalization;
38 using System.Reflection;
41 namespace System.Web.Script.Serialization
43 internal sealed class JsonDeserializer
45 /* Universal error constant */
47 const int UNIVERSAL_ERROR = __;
50 Characters are mapped into these 31 character classes. This allows for
51 a significant reduction in the size of the state transition table.
53 const int C_SPACE = 0x00; /* space */
54 const int C_WHITE = 0x01; /* other whitespace */
55 const int C_LCURB = 0x02; /* { */
56 const int C_RCURB = 0x03; /* } */
57 const int C_LSQRB = 0x04; /* [ */
58 const int C_RSQRB = 0x05; /* ] */
59 const int C_COLON = 0x06; /* : */
60 const int C_COMMA = 0x07; /* , */
61 const int C_QUOTE = 0x08; /* " */
62 const int C_BACKS = 0x09; /* \ */
63 const int C_SLASH = 0x0A; /* / */
64 const int C_PLUS = 0x0B; /* + */
65 const int C_MINUS = 0x0C; /* - */
66 const int C_POINT = 0x0D; /* . */
67 const int C_ZERO = 0x0E; /* 0 */
68 const int C_DIGIT = 0x0F; /* 123456789 */
69 const int C_LOW_A = 0x10; /* a */
70 const int C_LOW_B = 0x11; /* b */
71 const int C_LOW_C = 0x12; /* c */
72 const int C_LOW_D = 0x13; /* d */
73 const int C_LOW_E = 0x14; /* e */
74 const int C_LOW_F = 0x15; /* f */
75 const int C_LOW_L = 0x16; /* l */
76 const int C_LOW_N = 0x17; /* n */
77 const int C_LOW_R = 0x18; /* r */
78 const int C_LOW_S = 0x19; /* s */
79 const int C_LOW_T = 0x1A; /* t */
80 const int C_LOW_U = 0x1B; /* u */
81 const int C_ABCDF = 0x1C; /* ABCDF */
82 const int C_E = 0x1D; /* E */
83 const int C_ETC = 0x1E; /* everything else */
84 const int C_STAR = 0x1F; /* * */
85 const int C_I = 0x20; /* I */
86 const int C_LOW_I = 0x21; /* i */
87 const int C_LOW_Y = 0x22; /* y */
88 const int C_N = 0x23; /* N */
90 /* The state codes. */
91 const int GO = 0x00; /* start */
92 const int OK = 0x01; /* ok */
93 const int OB = 0x02; /* object */
94 const int KE = 0x03; /* key */
95 const int CO = 0x04; /* colon */
96 const int VA = 0x05; /* value */
97 const int AR = 0x06; /* array */
98 const int ST = 0x07; /* string */
99 const int ES = 0x08; /* escape */
100 const int U1 = 0x09; /* u1 */
101 const int U2 = 0x0A; /* u2 */
102 const int U3 = 0x0B; /* u3 */
103 const int U4 = 0x0C; /* u4 */
104 const int MI = 0x0D; /* minus */
105 const int ZE = 0x0E; /* zero */
106 const int IN = 0x0F; /* integer */
107 const int FR = 0x10; /* fraction */
108 const int E1 = 0x11; /* e */
109 const int E2 = 0x12; /* ex */
110 const int E3 = 0x13; /* exp */
111 const int T1 = 0x14; /* tr */
112 const int T2 = 0x15; /* tru */
113 const int T3 = 0x16; /* true */
114 const int F1 = 0x17; /* fa */
115 const int F2 = 0x18; /* fal */
116 const int F3 = 0x19; /* fals */
117 const int F4 = 0x1A; /* false */
118 const int N1 = 0x1B; /* nu */
119 const int N2 = 0x1C; /* nul */
120 const int N3 = 0x1D; /* null */
121 const int FX = 0x1E; /* *.* *eE* */
122 const int IV = 0x1F; /* invalid input */
123 const int UK = 0x20; /* unquoted key name */
124 const int UI = 0x21; /* ignore during unquoted key name construction */
125 const int I1 = 0x22; /* In */
126 const int I2 = 0x23; /* Inf */
127 const int I3 = 0x24; /* Infi */
128 const int I4 = 0x25; /* Infin */
129 const int I5 = 0x26; /* Infini */
130 const int I6 = 0x27; /* Infinit */
131 const int I7 = 0x28; /* Infinity */
132 const int V1 = 0x29; /* Na */
133 const int V2 = 0x2A; /* NaN */
136 const int FA = -10; /* false */
137 const int TR = -11; /* false */
138 const int NU = -12; /* null */
139 const int DE = -13; /* double detected by exponent e E */
140 const int DF = -14; /* double detected by fraction . */
141 const int SB = -15; /* string begin */
142 const int MX = -16; /* integer detected by minus */
143 const int ZX = -17; /* integer detected by zero */
144 const int IX = -18; /* integer detected by 1-9 */
145 const int EX = -19; /* next char is escaped */
146 const int UC = -20; /* Unicode character read */
147 const int SE = -4; /* string end */
148 const int AB = -5; /* array begin */
149 const int AE = -7; /* array end */
150 const int OS = -6; /* object start */
151 const int OE = -8; /* object end */
152 const int EO = -9; /* empty object */
153 const int CM = -3; /* comma */
154 const int CA = -2; /* colon action */
155 const int PX = -21; /* integer detected by plus */
156 const int KB = -22; /* unquoted key name begin */
157 const int UE = -23; /* unquoted key name end */
158 const int IF = -25; /* Infinity */
159 const int NN = -26; /* NaN */
187 This array maps the 128 ASCII characters into character classes.
188 The remaining Unicode characters should be mapped to C_ETC.
189 Non-whitespace control characters are errors.
191 static readonly int[] ascii_class = {
192 __, __, __, __, __, __, __, __,
193 __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __,
194 __, __, __, __, __, __, __, __,
195 __, __, __, __, __, __, __, __,
197 C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_QUOTE,
198 C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
199 C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
200 C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
202 C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,
203 C_ETC, C_I, C_ETC, C_ETC, C_ETC, C_ETC, C_N, C_ETC,
204 C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
205 C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,
207 C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
208 C_ETC, C_LOW_I, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,
209 C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,
210 C_ETC, C_LOW_Y, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC
213 static readonly int[,] state_transition_table = {
215 The state transition table takes the current state and the current symbol,
216 and returns either a new state or an action. An action is represented as a
217 negative number. A JSON text is accepted if at the end of the text the
218 state is OK and if the mode is MODE_DONE.
220 white ' 1-9 ABCDF etc
221 space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * I i y N */
222 /*start GO*/ {GO,GO,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,__,__,__,TR,__,__,__,__,__,I1,__,__,V1},
223 /*ok OK*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
224 /*object OB*/ {OB,OB,__,EO,__,__,__,__,SB,__,__,__,KB,__,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,__,KB,KB,KB,KB},
225 /*key KE*/ {KE,KE,__,__,__,__,__,__,SB,__,__,__,KB,__,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,__,KB,KB,KB,KB},
226 /*colon CO*/ {CO,CO,__,__,__,__,CA,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
227 /*value VA*/ {VA,VA,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
228 /*array AR*/ {AR,AR,OS,__,AB,AE,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
229 /*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,SE,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
230 /*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__,__,__,__,__},
231 /*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__,__,__,__,__},
232 /*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__,__,__,__,__},
233 /*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__,__,__,__,__},
234 /*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__,__,__,__,__},
235 /*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I1,__,__,__},
236 /*zero ZE*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
237 /*int IN*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,IN,IN,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__,__,__,__,__},
238 /*frac FR*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
239 /*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
240 /*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
241 /*exp E3*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
242 /*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__,__,__,__,__},
243 /*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__,__,__,__,__},
244 /*true T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
245 /*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
246 /*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__,__,__,__,__},
247 /*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__,__,__,__,__},
248 /*false F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
249 /*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__,__,__,__,__},
250 /*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__,__,__,__,__},
251 /*null N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__},
252 /*_. FX*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
253 /*inval. IV*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
254 /*unq.key UK*/ {UI,UI,__,__,__,__,UE,__,__,__,__,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,__,UK,UK,UK,UK},
255 /*unq.ign. UI*/ {UI,UI,__,__,__,__,UE,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
256 /*i1 I1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I2,__,__,__,__,__,__,__,__,__,__,__,__},
257 /*i2 I2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I3,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
258 /*i3 I3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I4,__,__},
259 /*i4 I4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I5,__,__,__,__,__,__,__,__,__,__,__,__},
260 /*i5 I5*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I6,__,__},
261 /*i6 I6*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I7,__,__,__,__,__,__,__,__,__},
262 /*i7 I7*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,IF,__},
263 /*v1 V1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,V2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
264 /*v2 V2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,NN},
267 JavaScriptSerializer serializer;
268 JavaScriptTypeResolver typeResolver;
274 Stack <JsonMode> modes;
275 Stack <object> returnValue;
280 Stack <string> currentKey;
281 StringBuilder buffer;
284 public JsonDeserializer (JavaScriptSerializer serializer)
286 this.serializer = serializer;
287 this.maxJsonLength = serializer.MaxJsonLength;
288 this.recursionLimit = serializer.RecursionLimit;
289 this.typeResolver = serializer.TypeResolver;
290 this.modes = new Stack <JsonMode> ();
291 this.currentKey = new Stack <string> ();
292 this.returnValue = new Stack <object> ();
294 this.currentPosition = 0;
295 this.recursionDepth = 0;
298 public object Deserialize (string input)
301 throw new ArgumentNullException ("input");
303 return Deserialize (new StringReader (input));
306 public object Deserialize (TextReader input)
309 throw new ArgumentNullException ("input");
312 buffer = new StringBuilder ();
315 value = input.Read ();
320 if (currentPosition > maxJsonLength)
321 throw new ArgumentException ("Maximum JSON input length has been exceeded.");
323 if (!ProcessCharacter ((char) value))
324 throw new InvalidOperationException ("JSON syntax error.");
327 object topObject = PeekObject ();
328 if (buffer.Length > 0) {
331 if (ParseBuffer (out result)) {
332 if (topObject != null)
339 if (returnValue.Count > 1)
340 throw new InvalidOperationException ("JSON syntax error.");
342 object ret = PopObject ();
347 void DumpObject (string indent, object obj)
349 if (obj is Dictionary <string, object>) {
350 Console.WriteLine (indent + "{");
351 foreach (KeyValuePair <string, object> kvp in (Dictionary <string, object>)obj) {
352 Console.WriteLine (indent + "\t\"{0}\": ", kvp.Key);
353 DumpObject (indent + "\t\t", kvp.Value);
355 Console.WriteLine (indent + "}");
356 } else if (obj is object[]) {
357 Console.WriteLine (indent + "[");
358 foreach (object o in (object[])obj)
359 DumpObject (indent + "\t", o);
360 Console.WriteLine (indent + "]");
361 } else if (obj != null)
362 Console.WriteLine (indent + obj.ToString ());
364 Console.WriteLine ("null");
368 void DecodeUnicodeChar ()
370 int len = buffer.Length;
372 throw new ArgumentException ("Invalid escaped unicode character specification (" + currentPosition + ")");
374 int code = Int32.Parse (buffer.ToString ().Substring (len - 4), NumberStyles.HexNumber);
375 buffer.Length = len - 6;
376 buffer.Append ((char)code);
379 string GetModeMessage (JsonMode expectedMode)
381 switch (expectedMode) {
383 return "Invalid array passed in, ',' or ']' expected (" + currentPosition + ")";
386 return "Invalid object passed in, key name or ':' expected (" + currentPosition + ")";
388 case JsonMode.OBJECT:
389 return "Invalid object passed in, key value expected (" + currentPosition + ")";
392 return "Invalid JSON string";
396 void PopMode (JsonMode expectedMode)
398 JsonMode mode = PeekMode ();
399 if (mode != expectedMode)
400 throw new ArgumentException (GetModeMessage (mode));
405 void PushMode (JsonMode newMode)
407 modes.Push (newMode);
412 if (modes.Count == 0)
413 return JsonMode.NONE;
415 return modes.Peek ();
418 void PushObject (object o)
420 returnValue.Push (o);
423 object PopObject (bool notIfLast)
425 int count = returnValue.Count;
429 if (notIfLast && count == 1)
432 return returnValue.Pop ();
437 return PopObject (false);
442 if (returnValue.Count == 0)
445 return returnValue.Peek ();
448 void RemoveLastCharFromBuffer ()
450 int len = buffer.Length;
453 buffer.Length = len - 1;
456 bool ParseBuffer (out object result)
460 if (jsonType == JsonType.NONE) {
465 string s = buffer.ToString ();
466 bool converted = true;
469 decimal decimalValue;
473 case JsonType.INTEGER:
474 /* MS AJAX.NET JSON parser promotes big integers to double */
476 if (Int32.TryParse (s, out intValue))
478 else if (Int64.TryParse (s, out longValue))
480 else if (Decimal.TryParse (s, out decimalValue))
481 result = decimalValue;
482 else if (Double.TryParse (s, out doubleValue))
483 result = doubleValue;
489 if (Decimal.TryParse (s, out decimalValue))
490 result = decimalValue;
491 else if (Double.TryParse (s, out doubleValue))
492 result = doubleValue;
498 if (String.Compare (s, "true", StringComparison.Ordinal) == 0)
505 if (String.Compare (s, "false", StringComparison.Ordinal) == 0)
512 if (String.Compare (s, "null", StringComparison.Ordinal) != 0)
516 case JsonType.STRING:
517 if (s.StartsWith ("/Date(", StringComparison.Ordinal) && s.EndsWith (")/", StringComparison.Ordinal)) {
518 long javaScriptTicks = Convert.ToInt64 (s.Substring (6, s.Length - 8));
519 result = new DateTime ((javaScriptTicks * 10000) + JsonSerializer.InitialJavaScriptDateTicks, DateTimeKind.Utc);
525 throw new InvalidOperationException (String.Format ("Internal error: unexpected JsonType ({0})", jsonType));
530 throw new ArgumentException ("Invalid JSON primitive: " + s);
536 bool ProcessCharacter (char ch)
538 int next_class, next_state;
543 next_class = ascii_class [ch];
544 if (next_class <= UNIVERSAL_ERROR)
550 RemoveLastCharFromBuffer ();
554 buffer.Append ('\b');
557 buffer.Append ('\f');
560 buffer.Append ('\n');
563 buffer.Append ('\r');
566 buffer.Append ('\t');
574 buffer.Append ("\\u");
579 } else if (jsonType != JsonType.NONE || !(next_class == C_SPACE || next_class == C_WHITE))
582 next_state = state_transition_table [state, next_class];
583 if (next_state >= 0) {
589 /* An action to perform */
590 switch (next_state) {
591 case UC: /* Unicode character */
592 DecodeUnicodeChar ();
596 case EX: /* Escaped character */
601 case MX: /* integer detected by minus */
602 jsonType = JsonType.INTEGER;
606 case PX: /* integer detected by plus */
607 jsonType = JsonType.INTEGER;
611 case ZX: /* integer detected by zero */
612 jsonType = JsonType.INTEGER;
616 case IX: /* integer detected by 1-9 */
617 jsonType = JsonType.INTEGER;
621 case DE: /* floating point number detected by exponent*/
622 jsonType = JsonType.FLOAT;
626 case DF: /* floating point number detected by fraction */
627 jsonType = JsonType.FLOAT;
631 case SB: /* string begin " or ' */
634 jsonType = JsonType.STRING;
638 case KB: /* unquoted key name begin */
639 jsonType = JsonType.STRING;
643 case UE: /* unquoted key name end ':' */
644 RemoveLastCharFromBuffer ();
645 if (ParseBuffer (out result))
647 jsonType = JsonType.NONE;
649 PopMode (JsonMode.KEY);
650 PushMode (JsonMode.OBJECT);
656 jsonType = JsonType.NULL;
661 jsonType = JsonType.FALSE;
666 jsonType = JsonType.TRUE;
670 case EO: /* empty } */
671 result = PopObject (true);
674 PopMode (JsonMode.KEY);
679 RemoveLastCharFromBuffer ();
681 if (ParseBuffer (out result))
684 result = PopObject (true);
688 PopMode (JsonMode.OBJECT);
690 jsonType = JsonType.NONE;
695 RemoveLastCharFromBuffer ();
696 if (ParseBuffer (out result))
698 PopMode (JsonMode.ARRAY);
699 result = PopObject (true);
703 jsonType = JsonType.NONE;
708 RemoveLastCharFromBuffer ();
710 PushMode (JsonMode.KEY);
716 RemoveLastCharFromBuffer ();
718 PushMode (JsonMode.ARRAY);
723 case SE: /* string end " or ' */
724 if (ch == quoteChar) {
725 RemoveLastCharFromBuffer ();
727 switch (PeekMode ()) {
729 if (ParseBuffer (out result))
732 jsonType = JsonType.NONE;
738 case JsonMode.OBJECT:
739 if (ParseBuffer (out result))
742 jsonType = JsonType.NONE;
746 case JsonMode.NONE: /* A stand-alone string */
747 jsonType = JsonType.STRING;
748 state = IV; /* the rest of input is invalid */
749 if (ParseBuffer (out result))
754 throw new ArgumentException ("Syntax error: string in unexpected place.");
760 RemoveLastCharFromBuffer ();
762 // With MS.AJAX, a comma resets the recursion depth
765 bool doStore = ParseBuffer (out result);
766 switch (PeekMode ()) {
767 case JsonMode.OBJECT:
770 PopMode (JsonMode.OBJECT);
771 PushMode (JsonMode.KEY);
772 jsonType = JsonType.NONE;
777 jsonType = JsonType.NONE;
784 throw new ArgumentException ("Syntax error: unexpected comma.");
790 RemoveLastCharFromBuffer ();
792 // With MS.AJAX a colon increases recursion depth
793 if (++recursionDepth >= recursionLimit)
794 throw new ArgumentException ("Recursion limit has been reached on parsing input.");
796 PopMode (JsonMode.KEY);
797 PushMode (JsonMode.OBJECT);
801 case IF: /* Infinity */
803 jsonType = JsonType.FLOAT;
804 switch (PeekMode ()) {
806 case JsonMode.OBJECT:
807 if (ParseBuffer (out result))
810 jsonType = JsonType.NONE;
814 case JsonMode.NONE: /* A stand-alone NaN/Infinity */
815 jsonType = JsonType.FLOAT;
816 state = IV; /* the rest of input is invalid */
817 if (ParseBuffer (out result))
822 throw new ArgumentException ("Syntax error: misplaced NaN/Infinity.");
828 throw new ArgumentException (GetModeMessage (PeekMode ()));
836 var arr = new ArrayList ();
842 var dict = new Dictionary <string, object> ();
846 void StoreKey (object o)
848 string key = o as string;
852 if (String.IsNullOrEmpty (key))
853 throw new InvalidOperationException ("Internal error: key is null, empty or not a string.");
855 currentKey.Push (key);
856 Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
858 throw new InvalidOperationException ("Internal error: current object is not a dictionary.");
860 /* MS AJAX.NET silently overwrites existing currentKey value */
864 void StoreValue (object o)
866 Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
868 ArrayList arr = PeekObject () as ArrayList;
870 throw new InvalidOperationException ("Internal error: current object is not a dictionary or an array.");
876 if (currentKey.Count == 0)
879 key = currentKey.Pop ();
881 if (String.IsNullOrEmpty (key))
882 throw new InvalidOperationException ("Internal error: object is a dictionary, but no key present.");