| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914 |
- //
- // JsonDeserializer.cs
- //
- // Author:
- // Marek Habersack <[email protected]>
- //
- // (C) 2008 Novell, Inc. http://novell.com/
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- // Code is based on JSON_checker (http://www.json.org/JSON_checker/) and JSON_parser
- // (http://fara.cs.uni-potsdam.de/~jsg/json_parser/) C sources. License for the original code
- // follows:
- #region Original License
- /*
- Copyright (c) 2005 JSON.org
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- The Software shall be used for Good, not Evil.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
- #endregion
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Data;
- using System.Globalization;
- using System.IO;
- using System.Reflection;
- using System.Text;
- namespace System.Web.Script.Serialization
- {
- internal sealed class JsonDeserializer
- {
- /* Universal error constant */
- const int __ = -1;
- const int UNIVERSAL_ERROR = __;
-
- /*
- Characters are mapped into these 31 character classes. This allows for
- a significant reduction in the size of the state transition table.
- */
- const int C_SPACE = 0x00; /* space */
- const int C_WHITE = 0x01; /* other whitespace */
- const int C_LCURB = 0x02; /* { */
- const int C_RCURB = 0x03; /* } */
- const int C_LSQRB = 0x04; /* [ */
- const int C_RSQRB = 0x05; /* ] */
- const int C_COLON = 0x06; /* : */
- const int C_COMMA = 0x07; /* , */
- const int C_QUOTE = 0x08; /* " */
- const int C_BACKS = 0x09; /* \ */
- const int C_SLASH = 0x0A; /* / */
- const int C_PLUS = 0x0B; /* + */
- const int C_MINUS = 0x0C; /* - */
- const int C_POINT = 0x0D; /* . */
- const int C_ZERO = 0x0E; /* 0 */
- const int C_DIGIT = 0x0F; /* 123456789 */
- const int C_LOW_A = 0x10; /* a */
- const int C_LOW_B = 0x11; /* b */
- const int C_LOW_C = 0x12; /* c */
- const int C_LOW_D = 0x13; /* d */
- const int C_LOW_E = 0x14; /* e */
- const int C_LOW_F = 0x15; /* f */
- const int C_LOW_L = 0x16; /* l */
- const int C_LOW_N = 0x17; /* n */
- const int C_LOW_R = 0x18; /* r */
- const int C_LOW_S = 0x19; /* s */
- const int C_LOW_T = 0x1A; /* t */
- const int C_LOW_U = 0x1B; /* u */
- const int C_ABCDF = 0x1C; /* ABCDF */
- const int C_E = 0x1D; /* E */
- const int C_ETC = 0x1E; /* everything else */
- const int C_STAR = 0x1F; /* * */
- const int C_I = 0x20; /* I */
- const int C_LOW_I = 0x21; /* i */
- const int C_LOW_Y = 0x22; /* y */
- const int C_N = 0x23; /* N */
-
- /* The state codes. */
- const int GO = 0x00; /* start */
- const int OK = 0x01; /* ok */
- const int OB = 0x02; /* object */
- const int KE = 0x03; /* key */
- const int CO = 0x04; /* colon */
- const int VA = 0x05; /* value */
- const int AR = 0x06; /* array */
- const int ST = 0x07; /* string */
- const int ES = 0x08; /* escape */
- const int U1 = 0x09; /* u1 */
- const int U2 = 0x0A; /* u2 */
- const int U3 = 0x0B; /* u3 */
- const int U4 = 0x0C; /* u4 */
- const int MI = 0x0D; /* minus */
- const int ZE = 0x0E; /* zero */
- const int IN = 0x0F; /* integer */
- const int FR = 0x10; /* fraction */
- const int E1 = 0x11; /* e */
- const int E2 = 0x12; /* ex */
- const int E3 = 0x13; /* exp */
- const int T1 = 0x14; /* tr */
- const int T2 = 0x15; /* tru */
- const int T3 = 0x16; /* true */
- const int F1 = 0x17; /* fa */
- const int F2 = 0x18; /* fal */
- const int F3 = 0x19; /* fals */
- const int F4 = 0x1A; /* false */
- const int N1 = 0x1B; /* nu */
- const int N2 = 0x1C; /* nul */
- const int N3 = 0x1D; /* null */
- const int FX = 0x1E; /* *.* *eE* */
- const int IV = 0x1F; /* invalid input */
- const int UK = 0x20; /* unquoted key name */
- const int UI = 0x21; /* ignore during unquoted key name construction */
- const int I1 = 0x22; /* In */
- const int I2 = 0x23; /* Inf */
- const int I3 = 0x24; /* Infi */
- const int I4 = 0x25; /* Infin */
- const int I5 = 0x26; /* Infini */
- const int I6 = 0x27; /* Infinit */
- const int I7 = 0x28; /* Infinity */
- const int V1 = 0x29; /* Na */
- const int V2 = 0x2A; /* NaN */
-
- /* Actions */
- const int FA = -10; /* false */
- const int TR = -11; /* false */
- const int NU = -12; /* null */
- const int DE = -13; /* double detected by exponent e E */
- const int DF = -14; /* double detected by fraction . */
- const int SB = -15; /* string begin */
- const int MX = -16; /* integer detected by minus */
- const int ZX = -17; /* integer detected by zero */
- const int IX = -18; /* integer detected by 1-9 */
- const int EX = -19; /* next char is escaped */
- const int UC = -20; /* Unicode character read */
- const int SE = -4; /* string end */
- const int AB = -5; /* array begin */
- const int AE = -7; /* array end */
- const int OS = -6; /* object start */
- const int OE = -8; /* object end */
- const int EO = -9; /* empty object */
- const int CM = -3; /* comma */
- const int CA = -2; /* colon action */
- const int PX = -21; /* integer detected by plus */
- const int KB = -22; /* unquoted key name begin */
- const int UE = -23; /* unquoted key name end */
- const int IF = -25; /* Infinity */
- const int NN = -26; /* NaN */
-
-
- enum JsonMode {
- NONE,
- ARRAY,
- DONE,
- KEY,
- OBJECT
- };
- enum JsonType {
- NONE = 0,
- ARRAY_BEGIN,
- ARRAY_END,
- OBJECT_BEGIN,
- OBJECT_END,
- INTEGER,
- FLOAT,
- NULL,
- TRUE,
- FALSE,
- STRING,
- KEY,
- MAX
- };
-
- /*
- This array maps the 128 ASCII characters into character classes.
- The remaining Unicode characters should be mapped to C_ETC.
- Non-whitespace control characters are errors.
- */
- static readonly int[] ascii_class = {
- __, __, __, __, __, __, __, __,
- __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __,
- __, __, __, __, __, __, __, __,
- __, __, __, __, __, __, __, __,
- C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_QUOTE,
- C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
- C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
- C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
- C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,
- C_ETC, C_I, C_ETC, C_ETC, C_ETC, C_ETC, C_N, C_ETC,
- C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
- C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,
- C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
- C_ETC, C_LOW_I, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,
- C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,
- C_ETC, C_LOW_Y, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC
- };
- static readonly int[,] state_transition_table = {
- /*
- The state transition table takes the current state and the current symbol,
- and returns either a new state or an action. An action is represented as a
- negative number. A JSON text is accepted if at the end of the text the
- state is OK and if the mode is MODE_DONE.
- white ' 1-9 ABCDF etc
- space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * I i y N */
- /*start GO*/ {GO,GO,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,__,__,__,TR,__,__,__,__,__,I1,__,__,V1},
- /*ok OK*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*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},
- /*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},
- /*colon CO*/ {CO,CO,__,__,__,__,CA,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*value VA*/ {VA,VA,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
- /*array AR*/ {AR,AR,OS,__,AB,AE,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
- /*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},
- /*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__,__,__,__,__},
- /*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__,__,__,__,__},
- /*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__,__,__,__,__},
- /*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__,__,__,__,__},
- /*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__,__,__,__,__},
- /*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I1,__,__,__},
- /*zero ZE*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*int IN*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,IN,IN,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__,__,__,__,__},
- /*frac FR*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
- /*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*exp E3*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__,__,__,__,__},
- /*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__,__,__,__,__},
- /*true T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__,__,__,__,__},
- /*false F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__,__,__,__,__},
- /*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*null N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*_. FX*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
- /*inval. IV*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*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},
- /*unq.ign. UI*/ {UI,UI,__,__,__,__,UE,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*i1 I1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I2,__,__,__,__,__,__,__,__,__,__,__,__},
- /*i2 I2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I3,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*i3 I3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I4,__,__},
- /*i4 I4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I5,__,__,__,__,__,__,__,__,__,__,__,__},
- /*i5 I5*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I6,__,__},
- /*i6 I6*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I7,__,__,__,__,__,__,__,__,__},
- /*i7 I7*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,IF,__},
- /*v1 V1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,V2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
- /*v2 V2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,NN},
- };
- JavaScriptSerializer serializer;
- JavaScriptTypeResolver typeResolver;
- int maxJsonLength;
- int currentPosition;
- int recursionLimit;
- int recursionDepth;
-
- Stack <JsonMode> modes;
- Stack <object> returnValue;
- JsonType jsonType;
-
- bool escaped;
- bool negated;
- int state;
- Stack <string> currentKey;
- StringBuilder buffer;
- char quoteChar;
-
- public JsonDeserializer (JavaScriptSerializer serializer)
- {
- this.serializer = serializer;
- this.maxJsonLength = serializer.MaxJsonLength;
- this.recursionLimit = serializer.RecursionLimit;
- this.typeResolver = serializer.TypeResolver;
- this.modes = new Stack <JsonMode> ();
- this.currentKey = new Stack <string> ();
- this.returnValue = new Stack <object> ();
- this.state = GO;
- this.currentPosition = 0;
- this.recursionDepth = 0;
- }
- public object Deserialize (string input)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- return Deserialize (new StringReader (input));
- }
-
- public object Deserialize (TextReader input)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
-
- int value;
- buffer = new StringBuilder ();
-
- while (true) {
- value = input.Read ();
- if (value < 0)
- break;
- currentPosition++;
- if (currentPosition > maxJsonLength)
- throw new ArgumentException ("Maximum JSON input length has been exceeded.");
- if (!ProcessCharacter ((char) value))
- throw new InvalidOperationException ("JSON syntax error.");
- }
- object topObject = PeekObject ();
- if (buffer.Length > 0) {
- object result;
- if (ParseBuffer (out result)) {
- if (topObject != null)
- StoreValue (result);
- else
- PushObject (result);
- }
- }
- if (returnValue.Count > 1)
- throw new InvalidOperationException ("JSON syntax error.");
- object ret = PopObject ();
- return ret;
- }
- #if DEBUG
- void DumpObject (string indent, object obj)
- {
- if (obj is Dictionary <string, object>) {
- Console.WriteLine (indent + "{");
- foreach (KeyValuePair <string, object> kvp in (Dictionary <string, object>)obj) {
- Console.WriteLine (indent + "\t\"{0}\": ", kvp.Key);
- DumpObject (indent + "\t\t", kvp.Value);
- }
- Console.WriteLine (indent + "}");
- } else if (obj is object[]) {
- Console.WriteLine (indent + "[");
- foreach (object o in (object[])obj)
- DumpObject (indent + "\t", o);
- Console.WriteLine (indent + "]");
- } else if (obj != null)
- Console.WriteLine (indent + obj.ToString ());
- else
- Console.WriteLine ("null");
- }
- #endif
-
- void DecodeUnicodeChar ()
- {
- int len = buffer.Length;
- if (len < 6)
- throw new ArgumentException ("Invalid escaped unicode character specification (" + currentPosition + ")");
- int code = Int32.Parse (buffer.ToString ().Substring (len - 4), NumberStyles.HexNumber);
- buffer.Length = len - 6;
- buffer.Append ((char)code);
- }
- string GetModeMessage (JsonMode expectedMode)
- {
- switch (expectedMode) {
- case JsonMode.ARRAY:
- return "Invalid array passed in, ',' or ']' expected (" + currentPosition + ")";
- case JsonMode.KEY:
- return "Invalid object passed in, key name or ':' expected (" + currentPosition + ")";
- case JsonMode.OBJECT:
- return "Invalid object passed in, key value expected (" + currentPosition + ")";
-
- default:
- return "Invalid JSON string";
- }
- }
-
- void PopMode (JsonMode expectedMode)
- {
- JsonMode mode = PeekMode ();
- if (mode != expectedMode)
- throw new ArgumentException (GetModeMessage (mode));
- modes.Pop ();
- }
- void PushMode (JsonMode newMode)
- {
- modes.Push (newMode);
- }
- JsonMode PeekMode ()
- {
- if (modes.Count == 0)
- return JsonMode.NONE;
- return modes.Peek ();
- }
- void PushObject (object o)
- {
- returnValue.Push (o);
- }
- object PopObject (bool notIfLast)
- {
- int count = returnValue.Count;
- if (count == 0)
- return null;
- if (notIfLast && count == 1)
- return null;
-
- return returnValue.Pop ();
- }
- object PopObject ()
- {
- return PopObject (false);
- }
-
- object PeekObject ()
- {
- if (returnValue.Count == 0)
- return null;
- return returnValue.Peek ();
- }
-
- void RemoveLastCharFromBuffer ()
- {
- int len = buffer.Length;
- if (len == 0)
- return;
- buffer.Length = len - 1;
- }
- bool ParseBuffer (out object result)
- {
- result = null;
-
- if (jsonType == JsonType.NONE) {
- buffer.Length = 0;
- return false;
- }
- string s = buffer.ToString ();
- bool converted = true;
- int intValue;
- long longValue;
- decimal decimalValue;
- double doubleValue;
-
- switch (jsonType) {
- case JsonType.INTEGER:
- /* MS AJAX.NET JSON parser promotes big integers to double */
-
- if (Int32.TryParse (s, out intValue))
- result = intValue;
- else if (Int64.TryParse (s, out longValue))
- result = longValue;
- else if (Decimal.TryParse (s, out decimalValue))
- result = decimalValue;
- else if (Double.TryParse (s, out doubleValue))
- result = doubleValue;
- else
- converted = false;
- break;
- case JsonType.FLOAT:
- if (Decimal.TryParse (s, out decimalValue))
- result = decimalValue;
- else if (Double.TryParse (s, out doubleValue))
- result = doubleValue;
- else
- converted = false;
- break;
- case JsonType.TRUE:
- if (String.Compare (s, "true", StringComparison.Ordinal) == 0)
- result = true;
- else
- converted = false;
- break;
- case JsonType.FALSE:
- if (String.Compare (s, "false", StringComparison.Ordinal) == 0)
- result = false;
- else
- converted = false;
- break;
- case JsonType.NULL:
- if (String.Compare (s, "null", StringComparison.Ordinal) != 0)
- converted = false;
- break;
- case JsonType.STRING:
- if (s.StartsWith ("/Date(", StringComparison.Ordinal) && s.EndsWith (")/", StringComparison.Ordinal)) {
- long javaScriptTicks = Convert.ToInt64 (s.Substring (6, s.Length - 8));
- result = new DateTime ((javaScriptTicks * 10000) + JsonSerializer.InitialJavaScriptDateTicks, DateTimeKind.Utc);
- } else
- result = s;
- break;
- default:
- throw new InvalidOperationException (String.Format ("Internal error: unexpected JsonType ({0})", jsonType));
-
- }
- if (!converted)
- throw new ArgumentException ("Invalid JSON primitive: " + s);
- buffer.Length = 0;
- return true;
- }
-
- bool ProcessCharacter (char ch)
- {
- int next_class, next_state;
- if (ch >= 128)
- next_class = C_ETC;
- else {
- next_class = ascii_class [ch];
- if (next_class <= UNIVERSAL_ERROR)
- return false;
- }
- if (escaped) {
- escaped = false;
- RemoveLastCharFromBuffer ();
-
- switch (ch) {
- case 'b':
- buffer.Append ('\b');
- break;
- case 'f':
- buffer.Append ('\f');
- break;
- case 'n':
- buffer.Append ('\n');
- break;
- case 'r':
- buffer.Append ('\r');
- break;
- case 't':
- buffer.Append ('\t');
- break;
- case '"':
- case '\\':
- case '/':
- buffer.Append (ch);
- break;
- case 'u':
- buffer.Append ("\\u");
- break;
- default:
- return false;
- }
- } else if (jsonType != JsonType.NONE || !(next_class == C_SPACE || next_class == C_WHITE))
- buffer.Append (ch);
- next_state = state_transition_table [state, next_class];
- if (next_state >= 0) {
- state = next_state;
- return true;
- }
- object result;
- /* An action to perform */
- switch (next_state) {
- case UC: /* Unicode character */
- DecodeUnicodeChar ();
- state = ST;
- break;
-
- case EX: /* Escaped character */
- escaped = true;
- state = ES;
- break;
- case MX: /* integer detected by minus */
- jsonType = JsonType.INTEGER;
- state = MI;
- break;
- case PX: /* integer detected by plus */
- jsonType = JsonType.INTEGER;
- state = MI;
- break;
-
- case ZX: /* integer detected by zero */
- jsonType = JsonType.INTEGER;
- state = ZE;
- break;
- case IX: /* integer detected by 1-9 */
- jsonType = JsonType.INTEGER;
- state = IN;
- break;
- case DE: /* floating point number detected by exponent*/
- jsonType = JsonType.FLOAT;
- state = E1;
- break;
- case DF: /* floating point number detected by fraction */
- jsonType = JsonType.FLOAT;
- state = FX;
- break;
- case SB: /* string begin " or ' */
- buffer.Length = 0;
- quoteChar = ch;
- jsonType = JsonType.STRING;
- state = ST;
- break;
- case KB: /* unquoted key name begin */
- jsonType = JsonType.STRING;
- state = UK;
- break;
- case UE: /* unquoted key name end ':' */
- RemoveLastCharFromBuffer ();
- if (ParseBuffer (out result))
- StoreKey (result);
- jsonType = JsonType.NONE;
-
- PopMode (JsonMode.KEY);
- PushMode (JsonMode.OBJECT);
- state = VA;
- buffer.Length = 0;
- break;
-
- case NU: /* n */
- jsonType = JsonType.NULL;
- state = N1;
- break;
- case FA: /* f */
- jsonType = JsonType.FALSE;
- state = F1;
- break;
- case TR: /* t */
- jsonType = JsonType.TRUE;
- state = T1;
- break;
- case EO: /* empty } */
- result = PopObject (true);
- if (result != null)
- StoreValue (result);
- PopMode (JsonMode.KEY);
- state = OK;
- break;
- case OE: /* } */
- RemoveLastCharFromBuffer ();
- if (ParseBuffer (out result))
- StoreValue (result);
- result = PopObject (true);
- if (result != null)
- StoreValue (result);
- PopMode (JsonMode.OBJECT);
- jsonType = JsonType.NONE;
- state = OK;
- break;
- case AE: /* ] */
- RemoveLastCharFromBuffer ();
- if (ParseBuffer (out result))
- StoreValue (result);
- PopMode (JsonMode.ARRAY);
- result = PopObject (true);
- if (result != null)
- StoreValue (result);
-
- jsonType = JsonType.NONE;
- state = OK;
- break;
- case OS: /* { */
- RemoveLastCharFromBuffer ();
- CreateObject ();
- PushMode (JsonMode.KEY);
-
- state = OB;
- break;
- case AB: /* [ */
- RemoveLastCharFromBuffer ();
- CreateArray ();
- PushMode (JsonMode.ARRAY);
- state = AR;
- break;
- case SE: /* string end " or ' */
- if (ch == quoteChar) {
- RemoveLastCharFromBuffer ();
- switch (PeekMode ()) {
- case JsonMode.KEY:
- if (ParseBuffer (out result))
- StoreKey (result);
- jsonType = JsonType.NONE;
- state = CO;
- buffer.Length = 0;
- break;
- case JsonMode.ARRAY:
- case JsonMode.OBJECT:
- if (ParseBuffer (out result))
- StoreValue (result);
-
- jsonType = JsonType.NONE;
- state = OK;
- break;
- case JsonMode.NONE: /* A stand-alone string */
- jsonType = JsonType.STRING;
- state = IV; /* the rest of input is invalid */
- if (ParseBuffer (out result))
- PushObject (result);
- break;
-
- default:
- throw new ArgumentException ("Syntax error: string in unexpected place.");
- }
- }
- break;
- case CM: /* , */
- RemoveLastCharFromBuffer ();
- // With MS.AJAX, a comma resets the recursion depth
- recursionDepth = 0;
- bool doStore = ParseBuffer (out result);
- switch (PeekMode ()) {
- case JsonMode.OBJECT:
- if (doStore)
- StoreValue (result);
- PopMode (JsonMode.OBJECT);
- PushMode (JsonMode.KEY);
- jsonType = JsonType.NONE;
- state = KE;
- break;
- case JsonMode.ARRAY:
- jsonType = JsonType.NONE;
- state = VA;
- if (doStore)
- StoreValue (result);
- break;
- default:
- throw new ArgumentException ("Syntax error: unexpected comma.");
- }
-
- break;
- case CA: /* : */
- RemoveLastCharFromBuffer ();
- // With MS.AJAX a colon increases recursion depth
- if (++recursionDepth >= recursionLimit)
- throw new ArgumentException ("Recursion limit has been reached on parsing input.");
-
- PopMode (JsonMode.KEY);
- PushMode (JsonMode.OBJECT);
- state = VA;
- break;
- case IF: /* Infinity */
- case NN: /* NaN */
- jsonType = JsonType.FLOAT;
- switch (PeekMode ()) {
- case JsonMode.ARRAY:
- case JsonMode.OBJECT:
- if (ParseBuffer (out result))
- StoreValue (result);
-
- jsonType = JsonType.NONE;
- state = OK;
- break;
- case JsonMode.NONE: /* A stand-alone NaN/Infinity */
- jsonType = JsonType.FLOAT;
- state = IV; /* the rest of input is invalid */
- if (ParseBuffer (out result))
- PushObject (result);
- break;
-
- default:
- throw new ArgumentException ("Syntax error: misplaced NaN/Infinity.");
- }
- buffer.Length = 0;
- break;
-
- default:
- throw new ArgumentException (GetModeMessage (PeekMode ()));
- }
-
- return true;
- }
- void CreateArray ()
- {
- var arr = new ArrayList ();
- PushObject (arr);
- }
-
- void CreateObject ()
- {
- var dict = new Dictionary <string, object> ();
- PushObject (dict);
- }
- void StoreKey (object o)
- {
- string key = o as string;
- if (key != null)
- key = key.Trim ();
-
- if (String.IsNullOrEmpty (key))
- throw new InvalidOperationException ("Internal error: key is null, empty or not a string.");
-
- currentKey.Push (key);
- Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
- if (dict == null)
- throw new InvalidOperationException ("Internal error: current object is not a dictionary.");
- /* MS AJAX.NET silently overwrites existing currentKey value */
- dict [key] = null;
- }
- void StoreValue (object o)
- {
- Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
- if (dict == null) {
- ArrayList arr = PeekObject () as ArrayList;
- if (arr == null)
- throw new InvalidOperationException ("Internal error: current object is not a dictionary or an array.");
- arr.Add (o);
- return;
- }
- string key;
- if (currentKey.Count == 0)
- key = null;
- else
- key = currentKey.Pop ();
- if (String.IsNullOrEmpty (key))
- throw new InvalidOperationException ("Internal error: object is a dictionary, but no key present.");
-
- dict [key] = o;
- }
- }
- }
|