using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.RegularExpressions; namespace SUISS.Cloud.Boomlagoon.JSON { public class JSONObject : IEnumerable>, IEnumerable { public JSONObject() { } public JSONObject(JSONObject other) { this.values = new Dictionary(); if (other != null) { foreach (KeyValuePair keyValuePair in other.values) { this.values[keyValuePair.Key] = new JSONValue(keyValuePair.Value); } } } public bool ContainsKey(string key) { return this.values.ContainsKey(key); } public JSONValue GetValue(string key) { JSONValue result; this.values.TryGetValue(key, out result); return result; } public string GetString(string key) { JSONValue value = this.GetValue(key); if (value == null) { JSONLogger.Error(key + "(string) == null"); return string.Empty; } return value.Str; } public double GetNumber(string key) { JSONValue value = this.GetValue(key); if (value == null) { JSONLogger.Error(key + " == null"); return double.NaN; } return value.Number; } public JSONObject GetObject(string key) { JSONValue value = this.GetValue(key); if (value == null) { JSONLogger.Error(key + " == null"); return null; } return value.Obj; } public bool GetBoolean(string key) { JSONValue value = this.GetValue(key); if (value == null) { JSONLogger.Error(key + " == null"); return false; } return value.Boolean; } public JSONArray GetArray(string key) { JSONValue value = this.GetValue(key); if (value == null) { JSONLogger.Error(key + " == null"); return null; } return value.Array; } public JSONValue this[string key] { get { return this.GetValue(key); } set { this.values[key] = value; } } public void Add(string key, JSONValue value) { this.values[key] = value; } public void Add(KeyValuePair pair) { this.values[pair.Key] = pair.Value; } public static JSONObject Parse(string jsonString) { if (string.IsNullOrEmpty(jsonString)) { return null; } JSONValue jsonvalue = null; List list = new List(); JSONObject.JSONParsingState jsonparsingState = JSONObject.JSONParsingState.Object; for (int i = 0; i < jsonString.Length; i++) { i = JSONObject.SkipWhitespace(jsonString, i); switch (jsonparsingState) { case JSONObject.JSONParsingState.Object: { if (jsonString[i] != '{') { return JSONObject.Fail('{', i); } JSONValue jsonvalue2 = new JSONObject(); if (jsonvalue != null) { jsonvalue2.Parent = jsonvalue; } jsonvalue = jsonvalue2; jsonparsingState = JSONObject.JSONParsingState.Key; break; } case JSONObject.JSONParsingState.Array: { if (jsonString[i] != '[') { return JSONObject.Fail('[', i); } JSONValue jsonvalue3 = new JSONArray(); if (jsonvalue != null) { jsonvalue3.Parent = jsonvalue; } jsonvalue = jsonvalue3; jsonparsingState = JSONObject.JSONParsingState.Value; break; } case JSONObject.JSONParsingState.EndObject: { if (jsonString[i] != '}') { return JSONObject.Fail('}', i); } if (jsonvalue.Parent == null) { return jsonvalue.Obj; } JSONValueType type = jsonvalue.Parent.Type; if (type != JSONValueType.Object) { if (type != JSONValueType.Array) { return JSONObject.Fail("valid object", i); } jsonvalue.Parent.Array.Add(new JSONValue(jsonvalue.Obj)); } else { jsonvalue.Parent.Obj.values[list.Pop()] = new JSONValue(jsonvalue.Obj); } jsonvalue = jsonvalue.Parent; jsonparsingState = JSONObject.JSONParsingState.ValueSeparator; break; } case JSONObject.JSONParsingState.EndArray: { if (jsonString[i] != ']') { return JSONObject.Fail(']', i); } if (jsonvalue.Parent == null) { return jsonvalue.Obj; } JSONValueType type2 = jsonvalue.Parent.Type; if (type2 != JSONValueType.Object) { if (type2 != JSONValueType.Array) { return JSONObject.Fail("valid object", i); } jsonvalue.Parent.Array.Add(new JSONValue(jsonvalue.Array)); } else { jsonvalue.Parent.Obj.values[list.Pop()] = new JSONValue(jsonvalue.Array); } jsonvalue = jsonvalue.Parent; jsonparsingState = JSONObject.JSONParsingState.ValueSeparator; break; } case JSONObject.JSONParsingState.Key: if (jsonString[i] == '}') { i--; jsonparsingState = JSONObject.JSONParsingState.EndObject; } else { string text = JSONObject.ParseString(jsonString, ref i); if (text == null) { return JSONObject.Fail("key string", i); } list.Add(text); jsonparsingState = JSONObject.JSONParsingState.KeyValueSeparator; } break; case JSONObject.JSONParsingState.Value: { char c = jsonString[i]; if (c == '"') { jsonparsingState = JSONObject.JSONParsingState.String; } else if (char.IsDigit(c) || c == '-') { jsonparsingState = JSONObject.JSONParsingState.Number; } else { switch (c) { case '[': jsonparsingState = JSONObject.JSONParsingState.Array; break; default: if (c != 'f') { if (c == 'n') { jsonparsingState = JSONObject.JSONParsingState.Null; break; } if (c != 't') { if (c != '{') { return JSONObject.Fail("beginning of value", i); } jsonparsingState = JSONObject.JSONParsingState.Object; break; } } jsonparsingState = JSONObject.JSONParsingState.Boolean; break; case ']': if (jsonvalue.Type != JSONValueType.Array) { return JSONObject.Fail("valid array", i); } jsonparsingState = JSONObject.JSONParsingState.EndArray; break; } } i--; break; } case JSONObject.JSONParsingState.KeyValueSeparator: if (jsonString[i] != ':') { return JSONObject.Fail(':', i); } jsonparsingState = JSONObject.JSONParsingState.Value; break; case JSONObject.JSONParsingState.ValueSeparator: { char c2 = jsonString[i]; if (c2 != ',') { if (c2 != ']') { if (c2 != '}') { return JSONObject.Fail(", } ]", i); } jsonparsingState = JSONObject.JSONParsingState.EndObject; i--; } else { jsonparsingState = JSONObject.JSONParsingState.EndArray; i--; } } else { jsonparsingState = ((jsonvalue.Type != JSONValueType.Object) ? JSONObject.JSONParsingState.Value : JSONObject.JSONParsingState.Key); } break; } case JSONObject.JSONParsingState.String: { string text2 = JSONObject.ParseString(jsonString, ref i); if (text2 == null) { return JSONObject.Fail("string value", i); } JSONValueType type3 = jsonvalue.Type; if (type3 != JSONValueType.Object) { if (type3 != JSONValueType.Array) { JSONLogger.Error("Fatal error, current JSON value not valid"); return null; } jsonvalue.Array.Add(text2); } else { jsonvalue.Obj.values[list.Pop()] = new JSONValue(text2); } jsonparsingState = JSONObject.JSONParsingState.ValueSeparator; break; } case JSONObject.JSONParsingState.Number: { double num = JSONObject.ParseNumber(jsonString, ref i); if (double.IsNaN(num)) { return JSONObject.Fail("valid number", i); } JSONValueType type4 = jsonvalue.Type; if (type4 != JSONValueType.Object) { if (type4 != JSONValueType.Array) { JSONLogger.Error("Fatal error, current JSON value not valid"); return null; } jsonvalue.Array.Add(num); } else { jsonvalue.Obj.values[list.Pop()] = new JSONValue(num); } jsonparsingState = JSONObject.JSONParsingState.ValueSeparator; break; } case JSONObject.JSONParsingState.Boolean: if (jsonString[i] == 't') { if (jsonString.Length < i + 4 || jsonString[i + 1] != 'r' || jsonString[i + 2] != 'u' || jsonString[i + 3] != 'e') { return JSONObject.Fail("true", i); } JSONValueType type5 = jsonvalue.Type; if (type5 != JSONValueType.Object) { if (type5 != JSONValueType.Array) { JSONLogger.Error("Fatal error, current JSON value not valid"); return null; } jsonvalue.Array.Add(new JSONValue(true)); } else { jsonvalue.Obj.values[list.Pop()] = new JSONValue(true); } i += 3; } else { if (jsonString.Length < i + 5 || jsonString[i + 1] != 'a' || jsonString[i + 2] != 'l' || jsonString[i + 3] != 's' || jsonString[i + 4] != 'e') { return JSONObject.Fail("false", i); } JSONValueType type6 = jsonvalue.Type; if (type6 != JSONValueType.Object) { if (type6 != JSONValueType.Array) { JSONLogger.Error("Fatal error, current JSON value not valid"); return null; } jsonvalue.Array.Add(new JSONValue(false)); } else { jsonvalue.Obj.values[list.Pop()] = new JSONValue(false); } i += 4; } jsonparsingState = JSONObject.JSONParsingState.ValueSeparator; break; case JSONObject.JSONParsingState.Null: if (jsonString[i] == 'n') { if (jsonString.Length < i + 4 || jsonString[i + 1] != 'u' || jsonString[i + 2] != 'l' || jsonString[i + 3] != 'l') { return JSONObject.Fail("null", i); } JSONValueType type7 = jsonvalue.Type; if (type7 != JSONValueType.Object) { if (type7 != JSONValueType.Array) { JSONLogger.Error("Fatal error, current JSON value not valid"); return null; } jsonvalue.Array.Add(new JSONValue(JSONValueType.Null)); } else { jsonvalue.Obj.values[list.Pop()] = new JSONValue(JSONValueType.Null); } i += 3; } jsonparsingState = JSONObject.JSONParsingState.ValueSeparator; break; } } JSONLogger.Error("Unexpected end of string"); return null; } private static int SkipWhitespace(string str, int pos) { while (pos < str.Length && char.IsWhiteSpace(str[pos])) { pos++; } return pos; } private static string ParseString(string str, ref int startPosition) { if (str[startPosition] != '"' || startPosition + 1 >= str.Length) { JSONObject.Fail('"', startPosition); return null; } int num = str.IndexOf('"', startPosition + 1); if (num <= startPosition) { JSONObject.Fail('"', startPosition + 1); return null; } while (str[num - 1] == '\\') { num = str.IndexOf('"', num + 1); if (num <= startPosition) { JSONObject.Fail('"', startPosition + 1); return null; } } string text = string.Empty; if (num > startPosition + 1) { text = str.Substring(startPosition + 1, num - startPosition - 1); } startPosition = num; for (;;) { Match match = JSONObject.unicodeRegex.Match(text); if (!match.Success) { break; } string text2 = match.Groups[1].Captures[0].Value; JSONObject.unicodeBytes[1] = byte.Parse(text2.Substring(0, 2), NumberStyles.HexNumber); JSONObject.unicodeBytes[0] = byte.Parse(text2.Substring(2, 2), NumberStyles.HexNumber); text2 = Encoding.Unicode.GetString(JSONObject.unicodeBytes, 0, JSONObject.unicodeBytes.Length); text = text.Replace(match.Value, text2); } return text; } private static double ParseNumber(string str, ref int startPosition) { if (startPosition >= str.Length || (!char.IsDigit(str[startPosition]) && str[startPosition] != '-')) { return double.NaN; } int num = startPosition + 1; while (num < str.Length && str[num] != ',' && str[num] != ']' && str[num] != '}') { num++; } double result; if (!double.TryParse(str.Substring(startPosition, num - startPosition), NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { return double.NaN; } startPosition = num - 1; return result; } private static JSONObject Fail(char expected, int position) { return JSONObject.Fail(new string(expected, 1), position); } private static JSONObject Fail(string expected, int position) { JSONLogger.Error(string.Format("Invalid json string, expecting {0} at {1}", expected, position)); return null; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); foreach (KeyValuePair keyValuePair in this.values) { stringBuilder.Append("\"" + keyValuePair.Key + "\""); stringBuilder.Append(':'); stringBuilder.Append(keyValuePair.Value.ToString()); stringBuilder.Append(','); } if (this.values.Count > 0) { stringBuilder.Remove(stringBuilder.Length - 1, 1); } stringBuilder.Append('}'); return stringBuilder.ToString(); } public IEnumerator> GetEnumerator() { return this.values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.values.GetEnumerator(); } public void Clear() { this.values.Clear(); } public void Remove(string key) { if (this.values.ContainsKey(key)) { this.values.Remove(key); } } private readonly IDictionary values = new Dictionary(); private static readonly Regex unicodeRegex = new Regex("\\\\u([0-9a-fA-F]{4})"); private static readonly byte[] unicodeBytes = new byte[2]; private enum JSONParsingState { Object, Array, EndObject, EndArray, Key, Value, KeyValueSeparator, ValueSeparator, String, Number, Boolean, Null } } }