Created
January 12, 2012 17:32
-
-
Save GarethJones/1601919 to your computer and use it in GitHub Desktop.
MiniJson Flash Unity version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// using UnityEngine; | |
using System; | |
using System.Collections; | |
using System.Text; | |
/* Based on the JSON parser from | |
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html | |
* | |
* I simplified it so that it doesn't throw exceptions | |
* and can be used in Unity iPhone with maximum code stripping. | |
*/ | |
/// <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 floats. | |
/// </summary> | |
public class MiniJSON | |
{ | |
public const int TOKEN_NONE = 0; | |
public const int TOKEN_CURLY_OPEN = 1; | |
public const int TOKEN_CURLY_CLOSE = 2; | |
public const int TOKEN_SQUARED_OPEN = 3; | |
public const int TOKEN_SQUARED_CLOSE = 4; | |
public const int TOKEN_COLON = 5; | |
public const int TOKEN_COMMA = 6; | |
public const int TOKEN_STRING = 7; | |
public const int TOKEN_NUMBER = 8; | |
public const int TOKEN_TRUE = 9; | |
public const int TOKEN_FALSE = 10; | |
public const int TOKEN_NULL = 11; | |
private const int BUILDER_CAPACITY = 2000; | |
protected static MiniJSON instance = new MiniJSON(); | |
/// <summary> | |
/// On decoding, this value holds the position at which the parse failed (-1 = no error). | |
/// </summary> | |
protected int lastErrorIndex = -1; | |
protected string lastDecode = ""; | |
/// <summary> | |
/// Parses the string json into a value | |
/// </summary> | |
/// <param name="json">A JSON string.</param> | |
/// <returns>An ArrayList, a Hashtable, a float, a string, null, true, or false</returns> | |
public static object JsonDecode(string json) | |
{ | |
// save the string for debug information | |
MiniJSON.instance.lastDecode = json; | |
if (json != null) { | |
char[] charArray = json.ToCharArray(); | |
int index = 0; | |
bool success = true; | |
object value = MiniJSON.instance.ParseValue(charArray, ref index, ref success); | |
if (success) { | |
MiniJSON.instance.lastErrorIndex = -1; | |
} else { | |
MiniJSON.instance.lastErrorIndex = index; | |
} | |
return value; | |
} else { | |
return null; | |
} | |
} | |
/// <summary> | |
/// Converts a Hashtable / ArrayList object into a JSON string | |
/// </summary> | |
/// <param name="json">A Hashtable / ArrayList</param> | |
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> | |
public static string JsonEncode(object json) | |
{ | |
StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); | |
bool success = MiniJSON.instance.SerializeValue(json, builder); | |
return (success ? builder.ToString() : null); | |
} | |
/// <summary> | |
/// On decoding, this function returns the position at which the parse failed (-1 = no error). | |
/// </summary> | |
/// <returns></returns> | |
public static bool LastDecodeSuccessful() | |
{ | |
return (MiniJSON.instance.lastErrorIndex == -1); | |
} | |
/// <summary> | |
/// On decoding, this function returns the position at which the parse failed (-1 = no error). | |
/// </summary> | |
/// <returns></returns> | |
public static int GetLastErrorIndex() | |
{ | |
return MiniJSON.instance.lastErrorIndex; | |
} | |
/// <summary> | |
/// If a decoding error occurred, this function returns a piece of the JSON string | |
/// at which the error took place. To ease debugging. | |
/// </summary> | |
/// <returns></returns> | |
public static string GetLastErrorSnippet() | |
{ | |
if (MiniJSON.instance.lastErrorIndex == -1) { | |
return ""; | |
} else { | |
int startIndex = MiniJSON.instance.lastErrorIndex - 5; | |
int endIndex = MiniJSON.instance.lastErrorIndex + 15; | |
if (startIndex < 0) { | |
startIndex = 0; | |
} | |
if (endIndex >= MiniJSON.instance.lastDecode.Length) { | |
endIndex = MiniJSON.instance.lastDecode.Length - 1; | |
} | |
return MiniJSON.instance.lastDecode.Substring(startIndex, endIndex - startIndex + 1); | |
} | |
} | |
protected Hashtable ParseObject(char[] json, ref int index) | |
{ | |
Hashtable table = new Hashtable(); | |
int token; | |
// { | |
NextToken(json, ref index); | |
bool done = false; | |
while (!done) { | |
token = LookAhead(json, index); | |
if (token == MiniJSON.TOKEN_NONE) { | |
return null; | |
} else if (token == MiniJSON.TOKEN_COMMA) { | |
NextToken(json, ref index); | |
} else if (token == MiniJSON.TOKEN_CURLY_CLOSE) { | |
NextToken(json, ref index); | |
return table; | |
} else { | |
// name | |
string name = ParseString(json, ref index); | |
if (name == null) { | |
return null; | |
} | |
// : | |
token = NextToken(json, ref index); | |
if (token != MiniJSON.TOKEN_COLON) { | |
return null; | |
} | |
// value | |
bool success = true; | |
object value = ParseValue(json, ref index, ref success); | |
if (!success) { | |
return null; | |
} | |
table[name] = value; | |
} | |
} | |
return table; | |
} | |
protected ArrayList ParseArray(char[] json, ref int index) | |
{ | |
ArrayList array = new ArrayList(); | |
// [ | |
NextToken(json, ref index); | |
bool done = false; | |
while (!done) { | |
int token = LookAhead(json, index); | |
if (token == MiniJSON.TOKEN_NONE) { | |
return null; | |
} else if (token == MiniJSON.TOKEN_COMMA) { | |
NextToken(json, ref index); | |
} else if (token == MiniJSON.TOKEN_SQUARED_CLOSE) { | |
NextToken(json, ref index); | |
break; | |
} else { | |
bool success = true; | |
object value = ParseValue(json, ref index, ref success); | |
if (!success) { | |
return null; | |
} | |
array.Add(value); | |
} | |
} | |
return array; | |
} | |
protected object ParseValue(char[] json, ref int index, ref bool success) | |
{ | |
switch (LookAhead(json, index)) { | |
case MiniJSON.TOKEN_STRING: | |
return ParseString(json, ref index); | |
case MiniJSON.TOKEN_NUMBER: | |
return ParseNumber(json, ref index); | |
case MiniJSON.TOKEN_CURLY_OPEN: | |
return ParseObject(json, ref index); | |
case MiniJSON.TOKEN_SQUARED_OPEN: | |
return ParseArray(json, ref index); | |
// case MiniJSON.TOKEN_TRUE: | |
// NextToken(json, ref index); | |
// return Boolean.Parse("TRUE"); | |
// case MiniJSON.TOKEN_FALSE: | |
// NextToken(json, ref index); | |
// return Boolean.Parse("FALSE"); | |
case MiniJSON.TOKEN_NULL: | |
NextToken(json, ref index); | |
return null; | |
case MiniJSON.TOKEN_NONE: | |
break; | |
} | |
success = false; | |
return null; | |
} | |
protected string ParseString(char[] json, ref int index) | |
{ | |
string s = ""; | |
char c; | |
EatWhitespace(json, ref index); | |
// " | |
c = json[index]; | |
index++; | |
bool complete = false; | |
while (!complete) { | |
if (index == json.Length) { | |
break; | |
} | |
c = json[index]; | |
index++; | |
if (c == '"') { | |
complete = true; | |
break; | |
} else if (c == '\\') { | |
if (index == json.Length) { | |
break; | |
} | |
c = json[index]; | |
index++; | |
if (c == '"') { | |
s += '"'; | |
} else if (c == '\\') { | |
s += '\\'; | |
} else if (c == '/') { | |
s += '/'; | |
} else if (c == 'b') { | |
s += '\b'; | |
} else if (c == 'f') { | |
s += '\f'; | |
} else if (c == 'n') { | |
s += '\n'; | |
} else if (c == 'r') { | |
s += '\r'; | |
} else if (c == 't') { | |
s += '\t'; | |
} else if (c == 'u') { | |
int remainingLength = json.Length - index; | |
if (remainingLength >= 4) { | |
char[] unicodeCharArray = new char[4]; | |
// Array.Copy(json, index, unicodeCharArray, 0, 4); | |
for ( int i=0; i<4; i++ ) | |
{ | |
unicodeCharArray[i] = json[index+i]; | |
} | |
// Drop in the HTML markup for the unicode character | |
s += "&#x" + new string(unicodeCharArray) + ";"; | |
/* | |
uint codePoint = UInt32.Parse(new string(unicodeCharArray), NumberStyles.HexNumber); | |
// convert the integer codepoint to a unicode char and add to string | |
s += Char.ConvertFromUtf32((int)codePoint); | |
*/ | |
// skip 4 chars | |
index += 4; | |
} else { | |
break; | |
} | |
} | |
} else { | |
s += c.ToString(); | |
} | |
} | |
if (!complete) { | |
return null; | |
} | |
return s; | |
} | |
protected float ParseNumber(char[] json, ref int index) | |
{ | |
EatWhitespace(json, ref index); | |
int lastIndex = GetLastIndexOfNumber(json, index); | |
int charLength = (lastIndex - index) + 1; | |
char[] numberCharArray = new char[charLength]; | |
// Array.Copy(json, index, numberCharArray, 0, charLength); | |
for ( int i=0; i<charLength; i++ ) | |
{ | |
numberCharArray[i] = json[index+i]; | |
} | |
index = lastIndex + 1; | |
return Single.Parse(new string(numberCharArray)); // , CultureInfo.InvariantCulture); | |
} | |
protected int GetLastIndexOfNumber(char[] json, int index) | |
{ | |
int lastIndex; | |
for (lastIndex = index; lastIndex < json.Length; lastIndex++) { | |
if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) { | |
break; | |
} | |
} | |
return lastIndex - 1; | |
} | |
protected void EatWhitespace(char[] json, ref int index) | |
{ | |
for (; index < json.Length; index++) { | |
if (" \t\n\r".IndexOf(json[index]) == -1) { | |
break; | |
} | |
} | |
} | |
protected int LookAhead(char[] json, int index) | |
{ | |
int saveIndex = index; | |
return NextToken(json, ref saveIndex); | |
} | |
protected int NextToken(char[] json, ref int index) | |
{ | |
EatWhitespace(json, ref index); | |
if (index == json.Length) { | |
return MiniJSON.TOKEN_NONE; | |
} | |
char c = json[index]; | |
index++; | |
switch (c) { | |
case '{': | |
return MiniJSON.TOKEN_CURLY_OPEN; | |
case '}': | |
return MiniJSON.TOKEN_CURLY_CLOSE; | |
case '[': | |
return MiniJSON.TOKEN_SQUARED_OPEN; | |
case ']': | |
return MiniJSON.TOKEN_SQUARED_CLOSE; | |
case ',': | |
return MiniJSON.TOKEN_COMMA; | |
case '"': | |
return MiniJSON.TOKEN_STRING; | |
case '0': case '1': case '2': case '3': case '4': | |
case '5': case '6': case '7': case '8': case '9': | |
case '-': | |
return MiniJSON.TOKEN_NUMBER; | |
case ':': | |
return MiniJSON.TOKEN_COLON; | |
} | |
index--; | |
int remainingLength = json.Length - index; | |
// false | |
if (remainingLength >= 5) { | |
if (json[index] == 'f' && | |
json[index + 1] == 'a' && | |
json[index + 2] == 'l' && | |
json[index + 3] == 's' && | |
json[index + 4] == 'e') { | |
index += 5; | |
return MiniJSON.TOKEN_FALSE; | |
} | |
} | |
// true | |
if (remainingLength >= 4) { | |
if (json[index] == 't' && | |
json[index + 1] == 'r' && | |
json[index + 2] == 'u' && | |
json[index + 3] == 'e') { | |
index += 4; | |
return MiniJSON.TOKEN_TRUE; | |
} | |
} | |
// null | |
if (remainingLength >= 4) { | |
if (json[index] == 'n' && | |
json[index + 1] == 'u' && | |
json[index + 2] == 'l' && | |
json[index + 3] == 'l') { | |
index += 4; | |
return MiniJSON.TOKEN_NULL; | |
} | |
} | |
return MiniJSON.TOKEN_NONE; | |
} | |
protected bool SerializeObjectOrArray(object objectOrArray, StringBuilder builder) | |
{ | |
if (objectOrArray is Hashtable) { | |
return SerializeObject((Hashtable)objectOrArray, builder); | |
} else if (objectOrArray is ArrayList) { | |
return SerializeArray((ArrayList)objectOrArray, builder); | |
} else { | |
return false; | |
} | |
} | |
protected bool SerializeObject(Hashtable anObject, StringBuilder builder) | |
{ | |
builder.Append("{"); | |
IDictionaryEnumerator e = anObject.GetEnumerator(); | |
bool first = true; | |
while (e.MoveNext()) { | |
string key = e.Key.ToString(); | |
object value = e.Value; | |
if (!first) { | |
builder.Append(", "); | |
} | |
SerializeString(key, builder); | |
builder.Append(":"); | |
if (!SerializeValue(value, builder)) { | |
return false; | |
} | |
first = false; | |
} | |
builder.Append("}"); | |
return true; | |
} | |
protected bool SerializeArray(ArrayList anArray, StringBuilder builder) | |
{ | |
builder.Append("["); | |
bool first = true; | |
for (int i = 0; i < anArray.Count; i++) { | |
object value = anArray[i]; | |
if (!first) { | |
builder.Append(", "); | |
} | |
if (!SerializeValue(value, builder)) { | |
return false; | |
} | |
first = false; | |
} | |
builder.Append("]"); | |
return true; | |
} | |
protected bool SerializeValue(object value, StringBuilder builder) | |
{ | |
// Type t = value.GetType(); | |
// Debug.Log("type: " + t.ToString() + " isArray: " + t.IsArray); | |
if (value.GetType().IsArray) { | |
SerializeArray(new ArrayList((ICollection) value), builder); | |
} else if (value is string) { | |
SerializeString((string)value, builder); | |
} else if (value is Char) { | |
SerializeString(value.ToString(), builder); | |
} else if (value is Hashtable) { | |
SerializeObject((Hashtable)value, builder); | |
} else if (value is ArrayList) { | |
SerializeArray((ArrayList)value, builder); | |
} | |
else if ((value is Boolean) && ((Boolean)value == true)) { | |
builder.Append("true"); | |
} else if ((value is Boolean) && ((Boolean)value == false)) { | |
builder.Append("false"); | |
} | |
// else if (value.GetType().IsPrimitive) { | |
else if (value is Single) { | |
SerializeNumber((Single)value, builder); | |
} else if (value == null) { | |
builder.Append("null"); | |
} else { | |
return false; | |
} | |
return true; | |
} | |
protected void SerializeString(string aString, StringBuilder builder) | |
{ | |
builder.Append("\""); | |
char[] charArray = aString.ToCharArray(); | |
for (int i = 0; i < charArray.Length; i++) { | |
char c = charArray[i]; | |
if (c == '"') { | |
builder.Append("\\\""); | |
} else if (c == '\\') { | |
builder.Append("\\\\"); | |
} else if (c == '\b') { | |
builder.Append("\\b"); | |
} else if (c == '\f') { | |
builder.Append("\\f"); | |
} else if (c == '\n') { | |
builder.Append("\\n"); | |
} else if (c == '\r') { | |
builder.Append("\\r"); | |
} else if (c == '\t') { | |
builder.Append("\\t"); | |
} else { | |
int codepoint = (Int32)c; | |
if ((codepoint >= 32) && (codepoint <= 126)) { | |
builder.Append(c); | |
} | |
// else | |
// { | |
// builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0')); | |
// } | |
} | |
} | |
builder.Append("\""); | |
} | |
protected void SerializeNumber(float number, StringBuilder builder) | |
{ | |
builder.Append(number.ToString()); // , CultureInfo.InvariantCulture)); | |
} | |
/* | |
/// <summary> | |
/// Determines if a given object is numeric in any way | |
/// (can be integer, float, etc). C# has no pretty way to do this. | |
/// </summary> | |
protected bool IsNumeric(object o) | |
{ | |
try { | |
Single.Parse(o.ToString()); | |
} catch (Exception) { | |
return false; | |
} | |
return true; | |
} | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment