|
- using System;
- using System.Collections;
- using System.Text;
- using System.Collections.Generic;
-
- namespace ETModel
- {
-
- /* 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 doubles.
- /// </summary>
-
-
- public class MiniJSON
- {
- 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;
- private const int BUILDER_CAPACITY = 2000;
-
- /// <summary>
- /// On decoding, this value holds the position at which the parse failed (-1 = no error).
- /// </summary>
- protected static int lastErrorIndex = -1;
- protected static string lastDecode = "";
-
-
- /// <summary>
- /// Parses the string json into a value
- /// </summary>
- /// <param name="json">A JSON string.</param>
- /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
- public static object jsonDecode(string json)
- {
- // save the string for debug information
- MiniJSON.lastDecode = json;
-
- if (json != null)
- {
- char[] charArray = json.ToCharArray();
- int index = 0;
- bool success = true;
- object value = MiniJSON.parseValue(charArray, ref index, ref success);
-
- if (success)
- MiniJSON.lastErrorIndex = -1;
- else
- MiniJSON.lastErrorIndex = index;
-
- return value;
- }
- else
- {
- return null;
- }
- }
-
-
- /// <summary>
- /// Converts a Hashtable / ArrayList / Dictionary(string,string) 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)
- {
- var builder = new StringBuilder(BUILDER_CAPACITY);
- var success = MiniJSON.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.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.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.lastErrorIndex == -1)
- {
- return "";
- }
- else
- {
- int startIndex = MiniJSON.lastErrorIndex - 5;
- int endIndex = MiniJSON.lastErrorIndex + 15;
- if (startIndex < 0)
- startIndex = 0;
-
- if (endIndex >= MiniJSON.lastDecode.Length)
- endIndex = MiniJSON.lastDecode.Length - 1;
-
- return MiniJSON.lastDecode.Substring(startIndex, endIndex - startIndex + 1);
- }
- }
-
-
- #region Parsing
-
- protected static 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 static 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 static 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 static string parseString(char[] json, ref int index)
- {
- string s = "";
- 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 == '\\')
- {
- if (index == json.Length)
- break;
-
- c = json[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);
-
- uint codePoint = UInt32.Parse(new string(unicodeCharArray), System.Globalization.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;
- }
-
- }
-
- if (!complete)
- return null;
-
- return s;
- }
-
-
- protected static double 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);
- index = lastIndex + 1;
- return Double.Parse(new string(numberCharArray)); // , CultureInfo.InvariantCulture);
- }
-
-
- protected static 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 static void eatWhitespace(char[] json, ref int index)
- {
- for (; index < json.Length; index++)
- if (" \t\n\r".IndexOf(json[index]) == -1)
- {
- break;
- }
- }
-
-
- protected static int lookAhead(char[] json, int index)
- {
- int saveIndex = index;
- return nextToken(json, ref saveIndex);
- }
-
-
- protected static 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;
- }
-
- #endregion
-
-
- #region Serialization
-
- protected static 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 static 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 static bool serializeDictionary(Dictionary<string, string> dict, StringBuilder builder)
- {
- builder.Append("{");
-
- bool first = true;
- foreach (var kv in dict)
- {
- if (!first)
- builder.Append(", ");
-
- serializeString(kv.Key, builder);
- builder.Append(":");
- serializeString(kv.Value, builder);
-
- first = false;
- }
-
- builder.Append("}");
- return true;
- }
-
-
- protected static 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 static bool serializeValue(object value, StringBuilder builder)
- {
- //Type t = value.GetType();
- //UnityEngine.Debug.Log("type: " + t.ToString() + " isArray: " + t.IsArray);
-
- if (value == null)
- {
- builder.Append("null");
- }
- else 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(Convert.ToString((char)value), builder);
- }
- else if (value is decimal)
- {
- serializeString(Convert.ToString((decimal)value), builder);
- }
- else if (value is Hashtable)
- {
- serializeObject((Hashtable)value, builder);
- }
- else if (value is Dictionary<string, string>)
- {
- serializeDictionary((Dictionary<string, string>)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)
- {
- serializeNumber(Convert.ToDouble(value), builder);
- }
- else
- {
- return false;
- }
-
- return true;
- }
-
-
- protected static 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 = Convert.ToInt32(c);
- if ((codepoint >= 32) && (codepoint <= 126))
- {
- builder.Append(c);
- }
- else
- {
- builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
- }
- }
- }
-
- builder.Append("\"");
- }
-
-
- protected static void serializeNumber(double number, StringBuilder builder)
- {
- builder.Append(Convert.ToString(number)); // , CultureInfo.InvariantCulture));
- }
-
- #endregion
-
- }
-
-
-
- #region Extension methods
-
- public static class MiniJsonExtensions
- {
- public static string toJson(this Hashtable obj)
- {
- return MiniJSON.jsonEncode(obj);
- }
-
-
- public static string toJson(this Dictionary<string, string> obj)
- {
- return MiniJSON.jsonEncode(obj);
- }
-
-
- public static ArrayList arrayListFromJson(this string json)
- {
- return MiniJSON.jsonDecode(json) as ArrayList;
- }
-
-
- public static Hashtable hashtableFromJson(this string json)
- {
- return MiniJSON.jsonDecode(json) as Hashtable;
- }
- }
-
- #endregion
-
- }
-
-
|