Xamarin Public Jenkins (auto-signing) e79aa3c0ed Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2016-08-03 10:59:49 +00:00

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,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, NumberStyles.Integer, CultureInfo.InvariantCulture, out intValue))
result = intValue;
else if (Int64.TryParse (s, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue))
result = longValue;
else if (Decimal.TryParse (s, NumberStyles.Integer, CultureInfo.InvariantCulture, out decimalValue))
result = decimalValue;
else if (Double.TryParse (s, NumberStyles.Integer, CultureInfo.InvariantCulture, out doubleValue))
result = doubleValue;
else
converted = false;
break;
case JsonType.FLOAT:
if (Decimal.TryParse (s, NumberStyles.Float, CultureInfo.InvariantCulture, out decimalValue))
result = decimalValue;
else if (Double.TryParse (s, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue))
result = doubleValue;
else
converted = false;
break;
case JsonType.TRUE:
if (String.Compare (s.Trim (), "true", StringComparison.Ordinal) == 0)
result = true;
else
converted = false;
break;
case JsonType.FALSE:
if (String.Compare (s.Trim (), "false", StringComparison.Ordinal) == 0)
result = false;
else
converted = false;
break;
case JsonType.NULL:
if (String.Compare (s.Trim (), "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;
}
}
}