You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			888 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			888 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //
 | |
| // JsonDeserializer.cs
 | |
| //
 | |
| // Author:
 | |
| //   Marek Habersack <grendel@twistedcode.net>
 | |
| //
 | |
| // (C) 2008 Novell, Inc.  http://novell.com/
 | |
| // Copyright 2011, Xamarin, Inc (http://xamarin.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.
 | |
| 
 | |
| 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;
 | |
| 		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;
 | |
| 		}
 | |
| 	}
 | |
| }
 |