diff --git a/JSON.cs b/JSON.cs
new file mode 100644
index 0000000..25046e4
--- /dev/null
+++ b/JSON.cs
@@ -0,0 +1,486 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.Text;
+
+namespace Procurios.Public
+{
+ ///
+ /// 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.
+ ///
+ public class JSON
+ {
+ 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;
+
+ ///
+ /// Parses the string json into a value
+ ///
+ /// A JSON string.
+ /// An ArrayList, a Hashtable, a double, a string, null, true, or false
+ public static object JsonDecode(string json)
+ {
+ bool success = true;
+
+ return JsonDecode(json, ref success);
+ }
+
+ ///
+ /// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
+ ///
+ /// A JSON string.
+ /// Successful parse?
+ /// An ArrayList, a Hashtable, a double, a string, null, true, or false
+ public static 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;
+ }
+ }
+
+ ///
+ /// Converts a Hashtable / ArrayList object into a JSON string
+ ///
+ /// A Hashtable / ArrayList
+ /// A JSON encoded string, or null if object 'json' is not serializable
+ public static string JsonEncode(object json)
+ {
+ StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
+ bool success = SerializeValue(json, builder);
+ return (success ? builder.ToString() : null);
+ }
+
+ protected static Hashtable ParseObject(char[] json, ref int index, ref bool success)
+ {
+ Hashtable table = new Hashtable();
+ int token;
+
+ // {
+ NextToken(json, ref index);
+
+ bool done = false;
+ while (!done) {
+ token = LookAhead(json, index);
+ if (token == JSON.TOKEN_NONE) {
+ success = false;
+ return null;
+ } else if (token == JSON.TOKEN_COMMA) {
+ NextToken(json, ref index);
+ } else if (token == JSON.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 != JSON.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;
+ }
+
+ protected static 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 == JSON.TOKEN_NONE) {
+ success = false;
+ return null;
+ } else if (token == JSON.TOKEN_COMMA) {
+ NextToken(json, ref index);
+ } else if (token == JSON.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;
+ }
+
+ protected static object ParseValue(char[] json, ref int index, ref bool success)
+ {
+ switch (LookAhead(json, index)) {
+ case JSON.TOKEN_STRING:
+ return ParseString(json, ref index, ref success);
+ case JSON.TOKEN_NUMBER:
+ return ParseNumber(json, ref index, ref success);
+ case JSON.TOKEN_CURLY_OPEN:
+ return ParseObject(json, ref index, ref success);
+ case JSON.TOKEN_SQUARED_OPEN:
+ return ParseArray(json, ref index, ref success);
+ case JSON.TOKEN_TRUE:
+ NextToken(json, ref index);
+ return true;
+ case JSON.TOKEN_FALSE:
+ NextToken(json, ref index);
+ return false;
+ case JSON.TOKEN_NULL:
+ NextToken(json, ref index);
+ return null;
+ case JSON.TOKEN_NONE:
+ break;
+ }
+
+ success = false;
+ return null;
+ }
+
+ protected static string ParseString(char[] json, ref int index, ref bool success)
+ {
+ StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
+ 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.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;
+ }
+ }
+
+ } else {
+ s.Append(c);
+ }
+
+ }
+
+ if (!complete) {
+ success = false;
+ return null;
+ }
+
+ return s.ToString();
+ }
+
+ protected static double ParseNumber(char[] json, ref int index, ref bool success)
+ {
+ EatWhitespace(json, ref index);
+
+ int lastIndex = GetLastIndexOfNumber(json, index);
+ int charLength = (lastIndex - index) + 1;
+
+ double number;
+ success = Double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
+
+ index = lastIndex + 1;
+ return number;
+ }
+
+ 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 JSON.TOKEN_NONE;
+ }
+
+ char c = json[index];
+ index++;
+ switch (c) {
+ case '{':
+ return JSON.TOKEN_CURLY_OPEN;
+ case '}':
+ return JSON.TOKEN_CURLY_CLOSE;
+ case '[':
+ return JSON.TOKEN_SQUARED_OPEN;
+ case ']':
+ return JSON.TOKEN_SQUARED_CLOSE;
+ case ',':
+ return JSON.TOKEN_COMMA;
+ case '"':
+ return JSON.TOKEN_STRING;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '-':
+ return JSON.TOKEN_NUMBER;
+ case ':':
+ return JSON.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 JSON.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 JSON.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 JSON.TOKEN_NULL;
+ }
+ }
+
+ return JSON.TOKEN_NONE;
+ }
+
+ protected static bool SerializeValue(object value, StringBuilder builder)
+ {
+ bool success = true;
+
+ if (value is string) {
+ success = SerializeString((string)value, builder);
+ } else if (value is Hashtable) {
+ success = SerializeObject((Hashtable)value, builder);
+ } else if (value is ArrayList) {
+ success = 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 is ValueType) {
+ // thanks to ritchie for pointing out ValueType to me
+ success = SerializeNumber(Convert.ToDouble(value), builder);
+ } else if (value == null) {
+ builder.Append("null");
+ } else {
+ success = false;
+ }
+ return success;
+ }
+
+ 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 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 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("\"");
+ return true;
+ }
+
+ protected static bool SerializeNumber(double number, StringBuilder builder)
+ {
+ builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
+ return true;
+ }
+ }
+}
+