using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
namespace fastJSON
{
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
/// All numbers are parsed to doubles.
/// </summary>
internal class JsonParser
{
private const int TOKEN_NONE = 0;
private const int TOKEN_CURLY_OPEN = 1;
private const int TOKEN_CURLY_CLOSE = 2;
private const int TOKEN_SQUARED_OPEN = 3;
private const int TOKEN_SQUARED_CLOSE = 4;
private const int TOKEN_COLON = 5;
private const int TOKEN_COMMA = 6;
private const int TOKEN_STRING = 7;
private const int TOKEN_NUMBER = 8;
private const int TOKEN_TRUE = 9;
private const int TOKEN_FALSE = 10;
private const int TOKEN_NULL = 11;
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An ArrayList, a dictionary, a double, a string, null, true, or false</returns>
internal object JsonDecode(string json)
{
bool success = true;
return JsonDecode(json, ref success);
}
/// <summary>
/// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
/// </summary>
/// <param name="json">A JSON string.</param>
/// <param name="success">Successful parse?</param>
/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
private object JsonDecode(string json, ref bool success)
{
success = true;
if (json != null) {
char[] charArray = json.ToCharArray();
int index = 0;
object value = ParseValue(charArray, ref index, ref success);
return value;
} else {
return null;
}
}
private Dictionary<string,object> ParseObject(char[] json, ref int index, ref bool success)
{
Dictionary<string,object> table = new Dictionary<string, object>();
int token;
// {
NextToken(json, ref index);
bool done = false;
while (!done) {
token = LookAhead(json, index);
if (token == TOKEN_NONE) {
success = false;
return null;
} else if (token == TOKEN_COMMA) {
NextToken(json, ref index);
} else if (token == TOKEN_CURLY_CLOSE) {
NextToken(json, ref index);
return table;
} else {
// name
string name = ParseString(json, ref index, ref success);
if (!success) {
success = false;
return null;
}
// :
token = NextToken(json, ref index);
if (token != TOKEN_COLON) {
success = false;
return null;
}
// value
object value = ParseValue(json, ref index, ref success);
if (!success) {
success = false;
return null;
}
table[name] = value;
}
}
return table;
}
private ArrayList ParseArray(char[] json, ref int index, ref bool success)
{
ArrayList array = new ArrayList();
NextToken(json, ref index);
bool done = false;
while (!done) {
int token = LookAhead(json, index);
if (token == TOKEN_NONE) {
success = false;
return null;
} else if (token == TOKEN_COMMA) {
NextToken(json, ref index);
} else if (token == TOKEN_SQUARED_CLOSE) {
NextToken(json, ref index);
break;
} else {
object value = ParseValue(json, ref index, ref success);
if (!success) {
return null;
}
array.Add(value);
}
}
return array;
}
private object ParseValue(char[] json, ref int index, ref bool success)
{
switch (LookAhead(json, index)) {
case TOKEN_NUMBER:
return ParseNumber(json, ref index, ref success);
case TOKEN_STRING:
return ParseString(json, ref index, ref success);
case TOKEN_CURLY_OPEN:
return ParseObject(json, ref index, ref success);
case TOKEN_SQUARED_OPEN:
return ParseArray(json, ref index, ref success);
case TOKEN_TRUE:
NextToken(json, ref index);
return true;
case TOKEN_FALSE:
NextToken(json, ref index);
return false;
case TOKEN_NULL:
NextToken(json, ref index);
return null;
case TOKEN_NONE:
break;
}
success = false;
return null;
}
private StringBuilder s = new StringBuilder();
private string ParseString(char[] json, ref int index, ref bool success)
{
s.Length = 0;
char c;
EatWhitespace(json, ref index);
// "
c = json[index++];
bool complete = false;
while (!complete) {
if (index == json.Length) {
break;
}
c = json[index++];
if (c == '"')
{
complete = true;
break;
}
else if (c != '\\')
s.Append(c);
else
{
if (index == json.Length)
break;
c = json[index++];
if (c == '"') s.Append('"');
else if (c == '\\') s.Append('\\');
else if (c == '/') s.Append('/');
else if (c == 'b') s.Append('\b');
else if (c == 'f') s.Append('\f');
else if (c == 'n') s.Append('\n');
else if (c == 'r') s.Append('\r');
else if (c == 't') s.Append('\t');
else if (c == 'u')
{
int remainingLength = json.Length - index;
if (remainingLength >= 4)
{
// parse the 32 bit hex into an integer codepoint
uint codePoint;
if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint)))
return "";
// convert the integer codepoint to a unicode char and add to string
s.Append(Char.ConvertFromUtf32((int)codePoint));
// skip 4 chars
index += 4;
}
else
break;
}
}
}
if (!complete) {
success = false;
return null;
}
return s.ToString();
}
private string ParseNumber(char[] json, ref int index, ref bool success)
{
EatWhitespace(json, ref index);
int lastIndex = GetLastIndexOfNumber(json, index);
int charLength = (lastIndex - index) + 1;
string number = new string(json,index,charLength);
success = true;
index = lastIndex + 1;
return number;
}
private int GetLastIndexOfNumber(char[] json, int index)
{
int lastIndex=index;
int len = json.Length;
while (lastIndex<len)
{
char c = json[lastIndex];
if((c>='0' && c<='9') || c=='+' || c=='-' || c=='.' || c=='e' || c=='E')
lastIndex++;
else
break;
}
return lastIndex-1;
}
private void EatWhitespace(char[] json, ref int index)
{
int len = json.Length;
while(index<len)
{
char c = json[index];
if(c == '\t' || c=='\r' || c== '\n' || c== ' ')
index++;
else
break;
}
}
private int LookAhead(char[] json, int index)
{
int saveIndex = index;
return NextToken(json, ref saveIndex);
}
private int NextToken(char[] json, ref int index)
{
EatWhitespace(json, ref index);
if (index == json.Length) {
return TOKEN_NONE;
}
char c = json[index];
index++;
switch (c) {
case '{':
return TOKEN_CURLY_OPEN;
case '}':
return TOKEN_CURLY_CLOSE;
case '[':
return TOKEN_SQUARED_OPEN;
case ']':
return TOKEN_SQUARED_CLOSE;
case ',':
return TOKEN_COMMA;
case '"':
return TOKEN_STRING;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '-':
return TOKEN_NUMBER;
case ':':
return TOKEN_COLON;
}
index--;
int remainingLength = json.Length - index;
char c1 = json[index];
char c2 = json[index+1];
char c3 = json[index+2];
char c4 = json[index+3];
char c5 = json[index+4];
// false
if (remainingLength >= 5) {
if (c1 == 'f' && c2 == 'a' && c3 == 'l' && c4 == 's' && c5 == 'e') {
index += 5;
return TOKEN_FALSE;
}
}
// true
if (remainingLength >= 4) {
if (c1 == 't' && c2 == 'r' && c3 == 'u' && c4 == 'e') {
index += 4;
return TOKEN_TRUE;
}
}
// null
if (remainingLength >= 4) {
if (c1 == 'n' && c2 == 'u' && c3 == 'l' && c4 == 'l') {
index += 4;
return TOKEN_NULL;
}
}
return TOKEN_NONE;
}
}
}