#define PRETTY //Comment out when you no longer need to read JSON to disable pretty Print system-wide //Using doubles will cause errors in VectorTemplates.cs; Unity speaks floats #define USEFLOAT //Use floats for numbers instead of doubles (enable if you're getting too many significant digits in string output) //#define POOLING //Currently using a build setting for this one (also it's experimental) using UnityEngine; using Debug = UnityEngine.Debug; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System; using System.Text; /* Copyright (c) 2015 Matt Schoen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Some modifications by Ramiro Oliva */ namespace WPM { public class JSONObject { #if POOLING const int MAX_POOL_SIZE = 10000; public static Queue releaseQueue = new Queue(); #endif const int MAX_DEPTH = 100; const string INFINITY = "\"INFINITY\""; const string NEGINFINITY = "\"NEGINFINITY\""; const string NaN = "\"NaN\""; public static readonly char[] WHITESPACE = { ' ', '\r', '\n', '\t', '\uFEFF', '\u0009' }; public enum Type { NULL, STRING, NUMBER, OBJECT, ARRAY, BOOL, BAKED } public bool isContainer { get { return (type == Type.ARRAY || type == Type.OBJECT); } } public Type type = Type.NULL; public int Count { get { if (list == null) return -1; return list.Count; } } public List list; public List keys; public string str; #if USEFLOAT public float n; public float f { get { return n; } } #else public double n; public float f { get { return (float)n; } } #endif /// /// Returns the DateTime representation. /// public DateTime d { get { return DateTime.FromBinary(i); } } public bool changes; public bool useInt; public long i; public bool b; public delegate void AddJSONContents(JSONObject self); public static JSONObject nullJO { get { return Create(Type.NULL); } } //an empty, null object public static JSONObject obj { get { return Create(Type.OBJECT); } } //an empty object public static JSONObject arr { get { return Create(Type.ARRAY); } } //an empty array public JSONObject(Type t) { type = t; switch (t) { case Type.ARRAY: list = new List(); break; case Type.OBJECT: list = new List(); keys = new List(); break; } } public JSONObject(bool b) { type = Type.BOOL; this.b = b; } #if USEFLOAT public JSONObject(float f) { type = Type.NUMBER; n = f; } #else public JSONObject(double d) { type = Type.NUMBER; n = d; } #endif public JSONObject(int i) { type = Type.NUMBER; this.i = i; useInt = true; n = i; } public JSONObject(long l) { type = Type.NUMBER; i = l; useInt = true; n = l; } public JSONObject(Dictionary dic) { type = Type.OBJECT; keys = new List(); list = new List(); //Not sure if it's worth removing the foreach here foreach (KeyValuePair kvp in dic) { keys.Add(kvp.Key); list.Add(CreateStringObject(kvp.Value)); } } public JSONObject(Dictionary dic) { type = Type.OBJECT; keys = new List(); list = new List(); //Not sure if it's worth removing the foreach here foreach (KeyValuePair kvp in dic) { keys.Add(kvp.Key); list.Add(kvp.Value); } } public JSONObject(AddJSONContents content) { content.Invoke(this); } public JSONObject(JSONObject[] objs) { type = Type.ARRAY; list = new List(objs); } //Convenience function for creating a JSONObject containing a string. This is not part of the constructor so that malformed JSON data doesn't just turn into a string object public static JSONObject StringObject(string val) { return CreateStringObject(val); } public void Absorb(JSONObject obj) { if (obj == null) return; if (obj.list != null) { if (list == null) list = new List(obj.list.Count); list.AddRange(obj.list); } if (obj.keys != null) { if (keys == null) keys = new List(obj.keys.Count); keys.AddRange(obj.keys); } str = obj.str; n = obj.n; useInt = obj.useInt; i = obj.i; b = obj.b; type = obj.type; } public static JSONObject Create() { #if POOLING JSONObject result = null; while(result == null && releaseQueue.Count > 0) { result = releaseQueue.Dequeue(); #if DEV //The following cases should NEVER HAPPEN (but they do...) if(result == null) Debug.WriteLine("wtf " + releaseQueue.Count); else if(result.list != null) Debug.WriteLine("wtflist " + result.list.Count); #endif } if(result != null) return result; #endif return new JSONObject(); } public static JSONObject Create(Type t) { JSONObject obj = Create(); obj.type = t; switch (t) { case Type.ARRAY: obj.list = new List(); break; case Type.OBJECT: obj.list = new List(); obj.keys = new List(); break; } return obj; } public static JSONObject Create(bool val) { JSONObject obj = Create(); obj.type = Type.BOOL; obj.b = val; return obj; } public static JSONObject Create(float val) { JSONObject obj = Create(); obj.type = Type.NUMBER; obj.n = val; return obj; } public static JSONObject Create(int val) { JSONObject obj = Create(); obj.type = Type.NUMBER; obj.n = val; obj.useInt = true; obj.i = val; return obj; } public static JSONObject Create(long val) { JSONObject obj = Create(); obj.type = Type.NUMBER; obj.n = val; obj.useInt = true; obj.i = val; return obj; } public static JSONObject CreateStringObject(string val) { JSONObject obj = Create(); obj.type = Type.STRING; obj.str = val; return obj; } public static JSONObject CreateBakedObject(string val) { JSONObject bakedObject = Create(); bakedObject.type = Type.BAKED; bakedObject.str = val; return bakedObject; } /// /// Create a JSONObject by parsing string data /// /// The string to be parsed /// The maximum depth for the parser to search. Set this to to 1 for the first level, /// 2 for the first 2 levels, etc. It defaults to -2 because -1 is the depth value that is parsed (see below) /// Whether to store levels beyond maxDepth in baked JSONObjects /// Whether to be strict in the parsing. For example, non-strict parsing will successfully /// parse "a string" into a string-type /// public static JSONObject Create(string val, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false) { JSONObject obj = Create(); obj.Parse(val, maxDepth, storeExcessLevels, strict); return obj; } public static JSONObject Create(AddJSONContents content) { JSONObject obj = Create(); content.Invoke(obj); return obj; } public static JSONObject Create(Dictionary dic) { JSONObject obj = Create(); obj.type = Type.OBJECT; obj.keys = new List(); obj.list = new List(); //Not sure if it's worth removing the foreach here foreach (KeyValuePair kvp in dic) { obj.keys.Add(kvp.Key); obj.list.Add(CreateStringObject(kvp.Value)); } return obj; } public JSONObject() { } #region PARSE public JSONObject(string str, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false) { //create a new JSONObject from a string (this will also create any children, and parse the whole string) Parse(str, maxDepth, storeExcessLevels, strict); } void Parse(string str, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false) { if (!string.IsNullOrEmpty(str)) { str = str.Trim(WHITESPACE); if (strict) { if (str[0] != '[' && str[0] != '{') { type = Type.NULL; Debug.LogWarning ("Improper (strict) JSON formatting. First character must be [ or {"); return; } } if (str.Length > 0) { #if UNITY_WP8 || UNITY_WSA if (str == "true") { type = Type.BOOL; b = true; } else if (str == "false") { type = Type.BOOL; b = false; } else if (str == "null") { type = Type.NULL; #else if (string.Compare(str, "true", true) == 0) { type = Type.BOOL; b = true; } else if (string.Compare(str, "false", true) == 0) { type = Type.BOOL; b = false; } else if (string.Compare(str, "null", true) == 0) { type = Type.NULL; #endif #if USEFLOAT } else if (str == INFINITY) { type = Type.NUMBER; n = float.PositiveInfinity; } else if (str == NEGINFINITY) { type = Type.NUMBER; n = float.NegativeInfinity; } else if (str == NaN) { type = Type.NUMBER; n = float.NaN; #else } else if(str == INFINITY) { type = Type.NUMBER; n = double.PositiveInfinity; } else if(str == NEGINFINITY) { type = Type.NUMBER; n = double.NegativeInfinity; } else if(str == NaN) { type = Type.NUMBER; n = double.NaN; #endif } else if (str[0] == '"') { type = Type.STRING; this.str = str.Substring(1, str.Length - 2); } else { int tokenTmp = 1; /* * Checking for the following formatting (www.json.org) * object - {"field1":value,"field2":value} * array - [value,value,value] * value - string - "string" * - number - 0.0 * - bool - true -or- false * - null - null */ int offset = 0; switch (str[offset]) { case '{': type = Type.OBJECT; keys = new List(); list = new List(); break; case '[': type = Type.ARRAY; list = new List(); break; default: try { #if USEFLOAT n = System.Convert.ToSingle(str); #else n = System.Convert.ToDouble(str); #endif if (!str.Contains(".")) { i = System.Convert.ToInt64(str); useInt = true; } type = Type.NUMBER; } catch (System.FormatException) { type = Type.NULL; Debug.LogWarning ("improper JSON formatting:" + str); } return; } string propName = ""; bool openQuote = false; bool inProp = false; int depth = 0; while (++offset < str.Length) { if (System.Array.IndexOf(WHITESPACE, str[offset]) > -1) continue; if (str[offset] == '\\') { offset += 1; continue; } if (str[offset] == '"') { if (openQuote) { if (!inProp && depth == 0 && type == Type.OBJECT) propName = str.Substring(tokenTmp + 1, offset - tokenTmp - 1); openQuote = false; } else { if (depth == 0 && type == Type.OBJECT) tokenTmp = offset; openQuote = true; } } if (openQuote) continue; if (type == Type.OBJECT && depth == 0) { if (str[offset] == ':') { tokenTmp = offset + 1; inProp = true; } } if (str[offset] == '[' || str[offset] == '{') { depth++; } else if (str[offset] == ']' || str[offset] == '}') { depth--; } //if (encounter a ',' at top level) || a closing ]/} if ((str[offset] == ',' && depth == 0) || depth < 0) { inProp = false; string inner = str.Substring(tokenTmp, offset - tokenTmp).Trim(WHITESPACE); if (inner.Length > 0) { if (type == Type.OBJECT) keys.Add(propName); if (maxDepth != -1) //maxDepth of -1 is the end of the line list.Add(Create(inner, (maxDepth < -1) ? -2 : maxDepth - 1)); else if (storeExcessLevels) list.Add(CreateBakedObject(inner)); } tokenTmp = offset + 1; } } } } else type = Type.NULL; } else type = Type.NULL; //If the string is missing, this is a null //Profiler.EndSample(); } #endregion public bool IsNumber { get { return type == Type.NUMBER; } } public bool IsNull { get { return type == Type.NULL; } } public bool IsString { get { return type == Type.STRING; } } public bool IsBool { get { return type == Type.BOOL; } } public bool IsArray { get { return type == Type.ARRAY; } } public bool IsObject { get { return type == Type.OBJECT || type == Type.BAKED; } } public void Add(bool val) { Add(Create(val)); } public void Add(float val) { Add(Create(val)); } public void Add(int val) { Add(Create(val)); } public void Add(string str) { Add(CreateStringObject(str)); } public void Add(AddJSONContents content) { Add(Create(content)); } public void Add(JSONObject obj) { if (obj) { //Don't do anything if the object is null if (type != Type.ARRAY) { type = Type.ARRAY; //Congratulations, son, you're an ARRAY now if (list == null) list = new List(); } list.Add(obj); changes = true; } } public void AddField(string name, bool val) { AddField(name, Create(val)); } public void AddField(string name, float val) { AddField(name, Create(val)); } public void AddField(string name, int val) { AddField(name, Create(val)); } public void AddField(string name, long val) { AddField(name, Create(val)); } public void AddField(string name, AddJSONContents content) { AddField(name, Create(content)); } public void AddField(string name, string val) { AddField(name, CreateStringObject(val)); } public void AddField(string name, JSONObject obj) { if (obj) { //Don't do anything if the object is null if (type != Type.OBJECT) { if (keys == null) keys = new List(); if (type == Type.ARRAY) { for (int i = 0; i < list.Count; i++) keys.Add(i + ""); } else if (list == null) list = new List(); type = Type.OBJECT; //Congratulations, son, you're an OBJECT now } keys.Add(name); list.Add(obj); changes = true; } } public void SetField(string name, string val) { SetField(name, CreateStringObject(val)); } public void SetField(string name, bool val) { SetField(name, Create(val)); } public void SetField(string name, float val) { SetField(name, Create(val)); } public void SetField(string name, int val) { SetField(name, Create(val)); } public void SetField(string name, JSONObject obj) { if (HasField(name)) { list[keys.IndexOf(name)] = obj; changes = true; return; // list.Remove (this [name]); // keys.Remove (name); } AddField(name, obj); } public void RemoveField(string name) { if (keys.IndexOf(name) > -1) { list.RemoveAt(keys.IndexOf(name)); keys.Remove(name); changes = true; } } public void RenameKey(string oldName, string newName) { // if new name exists, cancel int pos = keys.IndexOf(newName); if (pos >= 0) { return; } // if old name is not found, cancel pos = keys.IndexOf(oldName); if (pos < 0) { return; } keys[pos] = newName; changes = true; } public delegate void FieldNotFound(string name); public delegate void GetFieldResponse(JSONObject obj); public bool GetField(out bool field, string name, bool fallback) { field = fallback; return GetField(ref field, name); } public bool GetField(ref bool field, string name, FieldNotFound fail = null) { if (type == Type.OBJECT) { int index = keys.IndexOf(name); if (index >= 0) { field = list[index].b; return true; } } if (fail != null) fail.Invoke(name); return false; } #if USEFLOAT public bool GetField(out float field, string name, float fallback) { #else public bool GetField(out double field, string name, double fallback) { #endif field = fallback; return GetField(ref field, name); } #if USEFLOAT public bool GetField(ref float field, string name, FieldNotFound fail = null) { #else public bool GetField(ref double field, string name, FieldNotFound fail = null) { #endif if (type == Type.OBJECT) { int index = keys.IndexOf(name); if (index >= 0) { field = list[index].n; return true; } } if (fail != null) fail.Invoke(name); return false; } public bool GetField(out int field, string name, int fallback) { field = fallback; return GetField(ref field, name); } public bool GetField(ref int field, string name, FieldNotFound fail = null) { if (IsObject) { int index = keys.IndexOf(name); if (index >= 0) { field = (int)list[index].n; return true; } } if (fail != null) fail.Invoke(name); return false; } public bool GetField(out long field, string name, long fallback) { field = fallback; return GetField(ref field, name); } public bool GetField(ref long field, string name, FieldNotFound fail = null) { if (IsObject) { int index = keys.IndexOf(name); if (index >= 0) { field = (long)list[index].n; return true; } } if (fail != null) fail.Invoke(name); return false; } public bool GetField(out uint field, string name, uint fallback) { field = fallback; return GetField(ref field, name); } public bool GetField(ref uint field, string name, FieldNotFound fail = null) { if (IsObject) { int index = keys.IndexOf(name); if (index >= 0) { field = (uint)list[index].n; return true; } } if (fail != null) fail.Invoke(name); return false; } public bool GetField(out string field, string name, string fallback) { field = fallback; return GetField(ref field, name); } public bool GetField(ref string field, string name, FieldNotFound fail = null) { if (IsObject) { int index = keys.IndexOf(name); if (index >= 0) { field = list[index].str; return true; } } if (fail != null) fail.Invoke(name); return false; } public void GetField(string name, GetFieldResponse response, FieldNotFound fail = null) { if (response != null && IsObject) { int index = keys.IndexOf(name); if (index >= 0) { response.Invoke(list[index]); return; } } if (fail != null) fail.Invoke(name); } public JSONObject GetField(string name) { if (IsObject) for (int i = 0; i < keys.Count; i++) if (keys[i] == name) return list[i]; return null; } public bool HasFields(string[] names) { if (!IsObject) return false; for (int i = 0; i < names.Length; i++) if (!keys.Contains(names[i])) return false; return true; } public bool HasField(string name) { if (!IsObject) return false; for (int i = 0; i < keys.Count; i++) if (keys[i] == name) return true; return false; } public void Clear() { type = Type.NULL; if (list != null) list.Clear(); if (keys != null) keys.Clear(); str = ""; n = 0; b = false; changes = true; } /// /// Copy a JSONObject. This could probably work better /// /// public JSONObject Copy() { return Create(Print()); } /* * The Merge function is experimental. Use at your own risk. */ public void Merge(JSONObject obj) { MergeRecur(this, obj); changes = true; } /// /// Merge object right into left recursively /// /// The left (base) object /// The right (new) object static void MergeRecur(JSONObject left, JSONObject right) { if (left.type == Type.NULL) left.Absorb(right); else if (left.type == Type.OBJECT && right.type == Type.OBJECT) { for (int i = 0; i < right.list.Count; i++) { string key = right.keys[i]; if (right[i].isContainer) { if (left.HasField(key)) MergeRecur(left[key], right[i]); else left.AddField(key, right[i]); } else { if (left.HasField(key)) left.SetField(key, right[i]); else left.AddField(key, right[i]); } } } else if (left.type == Type.ARRAY && right.type == Type.ARRAY) { if (right.Count > left.Count) { Debug.LogError ("Cannot merge arrays when right object has more elements"); return; } for (int i = 0; i < right.list.Count; i++) { if (left[i].type == right[i].type) { //Only overwrite with the same type if (left[i].isContainer) MergeRecur(left[i], right[i]); else { left[i] = right[i]; } } } } } public void Bake() { if (type != Type.BAKED) { str = Print(); type = Type.BAKED; } } public IEnumerable BakeAsync() { if (type != Type.BAKED) { foreach (string s in PrintAsync()) { if (s == null) yield return s; else { str = s; } } type = Type.BAKED; } } #pragma warning disable 219 public string Print(bool pretty = false) { StringBuilder builder = new StringBuilder(); Stringify(0, builder, pretty); return builder.ToString(); } public IEnumerable PrintAsync(bool pretty = false) { StringBuilder builder = new StringBuilder(); printWatch.Reset(); printWatch.Start(); foreach (IEnumerable e in StringifyAsync(0, builder, pretty)) { yield return null; } yield return builder.ToString(); } #pragma warning restore 219 #region STRINGIFY const float maxFrameTime = 0.008f; static readonly Stopwatch printWatch = new Stopwatch(); IEnumerable StringifyAsync(int depth, StringBuilder builder, bool pretty = false) { //Convert the JSONObject into a string //Profiler.BeginSample("JSONprint"); if (depth++ > MAX_DEPTH) { Debug.Log ("reached max depth!"); yield break; } if (printWatch.Elapsed.TotalSeconds > maxFrameTime) { printWatch.Reset(); yield return null; printWatch.Start(); } switch (type) { case Type.BAKED: builder.Append(str); break; case Type.STRING: builder.AppendFormat("\"{0}\"", str); break; case Type.NUMBER: if (useInt) { builder.Append(i.ToString()); } else { #if USEFLOAT if (float.IsInfinity(n)) builder.Append(INFINITY); else if (float.IsNegativeInfinity(n)) builder.Append(NEGINFINITY); else if (float.IsNaN(n)) builder.Append(NaN); #else if(double.IsInfinity(n)) builder.Append(INFINITY); else if(double.IsNegativeInfinity(n)) builder.Append(NEGINFINITY); else if(double.IsNaN(n)) builder.Append(NaN); #endif else builder.Append(n.ToString()); } break; case Type.OBJECT: builder.Append("{"); if (list.Count > 0) { #if (PRETTY) //for a bit more readability, comment the define above to disable system-wide if (pretty) builder.Append("\n"); #endif for (int i = 0; i < list.Count; i++) { string key = keys[i]; JSONObject obj = list[i]; if (obj) { #if (PRETTY) if (pretty) for (int j = 0; j < depth; j++) builder.Append("\t"); //for a bit more readability #endif builder.AppendFormat("\"{0}\":", key); foreach (IEnumerable e in obj.StringifyAsync(depth, builder, pretty)) yield return e; builder.Append(","); #if (PRETTY) if (pretty) builder.Append("\n"); #endif } } #if (PRETTY) if (pretty) builder.Length -= 2; else #endif builder.Length--; } #if (PRETTY) if (pretty && list.Count > 0) { builder.Append("\n"); for (int j = 0; j < depth - 1; j++) builder.Append("\t"); //for a bit more readability } #endif builder.Append("}"); break; case Type.ARRAY: builder.Append("["); if (list.Count > 0) { #if (PRETTY) if (pretty) builder.Append("\n"); //for a bit more readability #endif for (int i = 0; i < list.Count; i++) { if (list[i]) { #if (PRETTY) if (pretty) for (int j = 0; j < depth; j++) builder.Append("\t"); //for a bit more readability #endif foreach (IEnumerable e in list[i].StringifyAsync(depth, builder, pretty)) yield return e; builder.Append(","); #if (PRETTY) if (pretty) builder.Append("\n"); //for a bit more readability #endif } } #if (PRETTY) if (pretty) builder.Length -= 2; else #endif builder.Length--; } #if (PRETTY) if (pretty && list.Count > 0) { builder.Append("\n"); for (int j = 0; j < depth - 1; j++) builder.Append("\t"); //for a bit more readability } #endif builder.Append("]"); break; case Type.BOOL: if (b) builder.Append("true"); else builder.Append("false"); break; case Type.NULL: builder.Append("null"); break; } //Profiler.EndSample(); } //TODO: Refactor Stringify functions to share core logic /* * I know, I know, this is really bad form. It turns out that there is a * significant amount of garbage created when calling as a coroutine, so this * method is duplicated. Hopefully there won't be too many future changes, but * I would still like a more elegant way to optionaly yield */ void Stringify(int depth, StringBuilder builder, bool pretty = false) { //Convert the JSONObject into a string //Profiler.BeginSample("JSONprint"); if (depth++ > MAX_DEPTH) { Debug.Log ("reached max depth!"); return; } switch (type) { case Type.BAKED: builder.Append(str); break; case Type.STRING: builder.AppendFormat("\"{0}\"", str); break; case Type.NUMBER: if (useInt) { builder.Append(i.ToString()); } else { #if USEFLOAT if (float.IsInfinity(n)) builder.Append(INFINITY); else if (float.IsNegativeInfinity(n)) builder.Append(NEGINFINITY); else if (float.IsNaN(n)) builder.Append(NaN); #else if(double.IsInfinity(n)) builder.Append(INFINITY); else if(double.IsNegativeInfinity(n)) builder.Append(NEGINFINITY); else if(double.IsNaN(n)) builder.Append(NaN); #endif else builder.Append(n.ToString()); } break; case Type.OBJECT: builder.Append("{"); if (list.Count > 0) { #if (PRETTY) //for a bit more readability, comment the define above to disable system-wide if (pretty) builder.Append("\n"); #endif for (int i = 0; i < list.Count; i++) { string key = keys[i]; JSONObject obj = list[i]; if (obj) { #if (PRETTY) if (pretty) for (int j = 0; j < depth; j++) builder.Append("\t"); //for a bit more readability #endif builder.AppendFormat("\"{0}\":", key); obj.Stringify(depth, builder, pretty); builder.Append(","); #if (PRETTY) if (pretty) builder.Append("\n"); #endif } } #if (PRETTY) if (pretty) builder.Length -= 2; else #endif builder.Length--; } #if (PRETTY) if (pretty && list.Count > 0) { builder.Append("\n"); for (int j = 0; j < depth - 1; j++) builder.Append("\t"); //for a bit more readability } #endif builder.Append("}"); break; case Type.ARRAY: builder.Append("["); if (list.Count > 0) { #if (PRETTY) if (pretty) builder.Append("\n"); //for a bit more readability #endif for (int i = 0; i < list.Count; i++) { if (list[i]) { #if (PRETTY) if (pretty) for (int j = 0; j < depth; j++) builder.Append("\t"); //for a bit more readability #endif list[i].Stringify(depth, builder, pretty); builder.Append(","); #if (PRETTY) if (pretty) builder.Append("\n"); //for a bit more readability #endif } } #if (PRETTY) if (pretty) builder.Length -= 2; else #endif builder.Length--; } #if (PRETTY) if (pretty && list.Count > 0) { builder.Append("\n"); for (int j = 0; j < depth - 1; j++) builder.Append("\t"); //for a bit more readability } #endif builder.Append("]"); break; case Type.BOOL: if (b) builder.Append("true"); else builder.Append("false"); break; case Type.NULL: builder.Append("null"); break; } //Profiler.EndSample(); } #endregion public static implicit operator WWWForm(JSONObject obj) { WWWForm form = new WWWForm(); for (int i = 0; i < obj.list.Count; i++) { string key = i + ""; if (obj.type == Type.OBJECT) key = obj.keys[i]; string val = obj.list[i].ToString(); if (obj.list[i].type == Type.STRING) val = val.Replace("\"", ""); form.AddField(key, val); } return form; } public JSONObject this[int index] { get { if (list.Count > index) return list[index]; return null; } set { if (list.Count > index) list[index] = value; changes = true; } } public JSONObject this[string index] { get { return GetField(index); } set { SetField(index, value); changes = true; } } #region implicit casting // added by kronnect public static implicit operator JSONObject(float f) { return new JSONObject(f); } public static implicit operator float(JSONObject json) { if (json == null) return float.NaN; return json.n; } public static implicit operator int(JSONObject json) { if (json == null) return 0; if (json.useInt) return (int)json.i; else return (int)json.n; } public static implicit operator long(JSONObject json) { if (json == null) return 0; if (json.useInt) return json.i; else return (long)json.n; } public static implicit operator JSONObject(string s) { return StringObject(s); } public static implicit operator string(JSONObject json) { if (json == null) return ""; switch (json.type) { case Type.BOOL: return json.b.ToString(); case Type.NULL: return ""; case Type.NUMBER: return json.n.ToString(); case Type.STRING: return json.str; } return json.Print(); } public static implicit operator JSONObject(DateTime d) { return new JSONObject(d.ToBinary()); } public static implicit operator DateTime(JSONObject json) { if (json == null) return new DateTime(); return DateTime.FromBinary((long)json.n); } public static implicit operator JSONObject(bool b) { return new JSONObject(b); } public static JSONObject FromArray(IList list) { JSONObject[] array = new JSONObject[list.Count]; for (int k = 0; k < list.Count; k++) { JSONObject o = new JSONObject(); o.Parse(list[k].ToString()); array[k] = o; } return new JSONObject(array); } public static JSONObject FromArray(object[] list) { JSONObject[] array = new JSONObject[list.Length]; for (int k = 0; k < list.Length; k++) { JSONObject o = new JSONObject(); o.Parse(list[k].ToString()); array[k] = o; } return new JSONObject(array); } public static implicit operator JSONObject(Color co) { JSONObject o = new JSONObject(); o.AddField("r", co.r); o.AddField("g", co.g); o.AddField("b", co.b); o.AddField("a", co.a); return o; } public static implicit operator Color(JSONObject json) { if (json == null || json.Count < 4) return Color.magenta; return new Color(json["r"].f, json["g"].f, json["b"].f, json["a"].f); } #endregion public override string ToString() { return Print(); } public string ToString(bool pretty) { return Print(pretty); } public Dictionary ToDictionary() { if (type == Type.OBJECT) { Dictionary result = new Dictionary(); for (int i = 0; i < list.Count; i++) { JSONObject val = list[i]; switch (val.type) { case Type.STRING: result.Add(keys[i], val.str); break; case Type.NUMBER: result.Add(keys[i], val.n + ""); break; case Type.BOOL: result.Add(keys[i], val.b + ""); break; default: Debug.LogWarning ("Omitting object: " + keys[i] + " in dictionary conversion"); break; } } return result; } Debug.Log ("Tried to turn non-Object JSONObject into a dictionary"); return null; } public static implicit operator bool(JSONObject o) { return o != null; } #if POOLING static bool pool = true; public static void ClearPool() { pool = false; releaseQueue.Clear(); pool = true; } ~JSONObject() { if(pool && releaseQueue.Count < MAX_POOL_SIZE) { type = Type.NULL; list = null; keys = null; str = ""; n = 0; b = false; releaseQueue.Enqueue(this); } } #endif } }