ro-webgl/Assets/Src/Core/Json/JSONObject.cs
2021-12-21 09:40:39 +08:00

1322 lines
32 KiB
C#

#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 System.Diagnostics;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Debug = UnityEngine.Debug;
/*
* http://www.opensource.org/licenses/lgpl-2.1.php
* JSONObject class
* for use with Unity
* Copyright Matt Schoen 2010 - 2013
*/
public class JSONObject
{
#if POOLING
const int MAX_POOL_SIZE = 10000;
public static Queue<JSONObject> releaseQueue = new Queue<JSONObject>();
#endif
const int MAX_DEPTH = 100;
const string INFINITY = "\"INFINITY\"";
const string NEGINFINITY = "\"NEGINFINITY\"";
const string NaN = "\"NaN\"";
static readonly char[] WHITESPACE = new[] { ' ', '\r', '\n', '\t' };
public enum Type
{
NULL,
STRING,
NUMBER,
LONG,
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<JSONObject> list;
public List<string> keys;
public string str;
#if USEFLOAT
long nValue;
float fValue;
public long n {
get { return nValue; }
set {
nValue = value;
fValue = nValue;
isIntNumber = true;
}
}
public int i { get { return (int)n; } }
public long l { get { return n; } set { n = value; } }
public float f {
get { return fValue; }
set {
fValue = value;
nValue = (int)fValue;
isIntNumber = false;
}
}
public bool isIntNumber { get; private set; }
#else
public double n;
public float f {
get {
return (float)n;
}
}
#endif
public bool b;
public delegate void AddJSONConents (JSONObject self);
public static JSONObject nullJO { get { return Create (Type.NULL); } }
//an empty, null object
public static JSONObject newObj { 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<JSONObject> ();
break;
case Type.OBJECT:
list = new List<JSONObject> ();
keys = new List<string> ();
break;
}
}
public JSONObject (bool b)
{
type = Type.BOOL;
this.b = b;
}
public JSONObject (long l)
{
type = Type.LONG;
this.l = l;
}
#if USEFLOAT
public JSONObject (float f)
{
type = Type.NUMBER;
this.f = f;
}
#else
public JSONObject(double d) {
type = Type.NUMBER;
n = d;
}
#endif
public JSONObject (Dictionary<string, string> dic)
{
type = Type.OBJECT;
keys = new List<string> ();
list = new List<JSONObject> ();
//Not sure if it's worth removing the foreach here
foreach (KeyValuePair<string, string> kvp in dic) {
keys.Add (kvp.Key);
list.Add (CreateStringObject (kvp.Value));
}
}
public JSONObject (Dictionary<string, JSONObject> dic)
{
type = Type.OBJECT;
keys = new List<string> ();
list = new List<JSONObject> ();
//Not sure if it's worth removing the foreach here
foreach (KeyValuePair<string, JSONObject> kvp in dic) {
keys.Add (kvp.Key);
list.Add (kvp.Value);
}
}
public JSONObject (AddJSONConents content)
{
content.Invoke (this);
}
public JSONObject (JSONObject[] objs)
{
type = Type.ARRAY;
list = new List<JSONObject> (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)
{
list.AddRange (obj.list);
keys.AddRange (obj.keys);
str = obj.str;
#if USEFLOAT
nValue = obj.nValue;
fValue = obj.fValue;
#else
n = obj.n;
#endif
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.Log("wtf " + releaseQueue.Count);
else if(result.list != null)
Debug.Log("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<JSONObject> ();
break;
case Type.OBJECT:
Obj.list = new List<JSONObject> ();
Obj.keys = new List<string> ();
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;
#if USEFLOAT
Obj.f = val;
#else
Obj.n = val;
#endif
return Obj;
}
public static JSONObject Create (int val)
{
JSONObject Obj = Create ();
Obj.type = Type.NUMBER;
Obj.n = val;
return Obj;
}
public static JSONObject Create(long val)
{
JSONObject Obj = Create();
Obj.type = Type.LONG;
Obj.l = 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;
}
/// <summary>
/// Create a JSONObject by parsing string data
/// </summary>
/// <param name="val">The string to be parsed</param>
/// <param name="maxDepth">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)</param>
/// <param name="storeExcessLevels">Whether to store levels beyond maxDepth in baked JSONObjects</param>
/// <param name="strict">Whether to be strict in the parsing. For example, non-strict parsing will successfully
/// parse "a string" into a string-type </param>
/// <returns></returns>
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 (AddJSONConents content)
{
JSONObject Obj = Create ();
content.Invoke (Obj);
return Obj;
}
public static JSONObject Create (Dictionary<string, string> dic)
{
JSONObject Obj = Create ();
Obj.type = Type.OBJECT;
Obj.keys = new List<string> ();
Obj.list = new List<JSONObject> ();
//Not sure if it's worth removing the foreach here
foreach (KeyValuePair<string, string> kvp in dic) {
Obj.keys.Add (kvp.Key);
Obj.list.Add (CreateStringObject (kvp.Value));
}
return Obj;
}
public static JSONObject Create (int[] vals)
{
JSONObject resultObj = Create ();
resultObj.type = Type.ARRAY;
resultObj.list = new List<JSONObject> ();
foreach (int v in vals)
resultObj.list.Add (Create (v));
return resultObj;
}
public static JSONObject CreateArrayJson (params int[] param)
{
return Create (param);
}
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);
}
// public static bool output_debug = false;
void Parse (string inStr, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false)
{
if (!string.IsNullOrEmpty (inStr)) {
inStr = inStr.Trim (WHITESPACE);
if (strict) {
if (inStr [0] != '[' && inStr [0] != '{') {
type = Type.NULL;
Debug.LogWarning ("Improper (strict) JSON formatting. First character must be [ or {");
return;
}
}
if (inStr.Length > 0) {
if (string.Compare (inStr, "true", true) == 0) {
type = Type.BOOL;
b = true;
} else if (string.Compare (inStr, "false", true) == 0) {
type = Type.BOOL;
b = false;
} else if (string.Compare (inStr, "null", true) == 0) {
type = Type.NULL;
//#if USEFLOAT
} else if (inStr == INFINITY) {
type = Type.NUMBER;
f = float.PositiveInfinity;
} else if (inStr == NEGINFINITY) {
type = Type.NUMBER;
f = float.NegativeInfinity;
} else if (inStr == NaN) {
type = Type.NUMBER;
f = 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 (inStr [0] == '"') {
type = Type.STRING;
str = inStr.Substring (1, inStr.Length - 2);
if (str.Length > 0 && str.Contains ("\\u")) {
str = UnicodeUtil.Convert (str);
}
} 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 (inStr [offset]) {
case '{':
type = Type.OBJECT;
keys = new List<string> ();
list = new List<JSONObject> ();
break;
case '[':
type = Type.ARRAY;
list = new List<JSONObject> ();
break;
default:
try {
#if USEFLOAT
if (inStr.IndexOf ('.') >= 0 || inStr.IndexOf ('e') >= 0 || inStr.IndexOf ('E') >= 0) {
f = System.Convert.ToSingle (inStr);
} else {
n = System.Convert.ToInt64 (inStr);
}
#else
n = System.Convert.ToDouble(str);
#endif
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 < inStr.Length) {
if (System.Array.IndexOf (WHITESPACE, inStr [offset]) > -1)
continue;
if (inStr [offset] == '\\')
offset += 2;
if (inStr [offset] == '"') {
if (openQuote) {
if (!inProp && depth == 0 && type == Type.OBJECT)
propName = inStr.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 (inStr [offset] == ':') {
tokenTmp = offset + 1;
inProp = true;
}
}
if (inStr [offset] == '[' || inStr [offset] == '{') {
depth++;
} else if (inStr [offset] == ']' || inStr [offset] == '}') {
depth--;
}
//if (encounter a ',' at top level) || a closing ]/}
if ((inStr [offset] == ',' && depth == 0) || depth < 0) {
inProp = false;
string inner = inStr.Substring (tokenTmp, offset - tokenTmp).Trim (WHITESPACE);
if (inner.Length > 0) {
// if (output_debug) {
// Log.D ("{0}", inner);
// if (inner.CompareTo ("1442809048") == 0)
// Log.D ("{0}", inner);
// }
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; } }
public void Add (bool val)
{
Add (Create (val));
}
public void Add (float val)
{
Add (Create (val));
}
public void Add(long val)
{
Add (Create(val));
}
public void Add (int val)
{
Add (Create (val));
}
public void Add (string str)
{
Add (CreateStringObject (str));
}
public void Add (AddJSONConents 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<JSONObject> ();
}
list.Add (obj);
}
}
public void AddBoolField (string name, bool val)
{
AddField (name, Create (val));
}
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, AddJSONConents 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<string> ();
if (type == Type.ARRAY) {
for (int i = 0; i < list.Count; i++)
keys.Add (i + "");
} else if (list == null)
list = new List<JSONObject> ();
type = Type.OBJECT; //Congratulations, son, you're an OBJECT now
}
keys.Add (name);
list.Add (obj);
}
}
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.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);
}
}
public delegate void FieldNotFound (string name);
public delegate void GetFieldResponse (JSONObject obj);
public void 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;
}
}
if (fail != null)
fail.Invoke (name);
}
//#if USEFLOAT
public void GetField (ref float field, string name, FieldNotFound fail = null)
{
//#else
//public void 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].f;
return;
}
}
if (fail != null)
fail.Invoke (name);
}
public void GetField (ref int field, string name, FieldNotFound fail = null)
{
if (type == Type.OBJECT) {
int index = keys.IndexOf (name);
if (index >= 0) {
field = (int)list [index].n;
return;
}
}
if (fail != null)
fail.Invoke (name);
}
public void GetField (ref uint field, string name, FieldNotFound fail = null)
{
if (type == Type.OBJECT) {
int index = keys.IndexOf (name);
if (index >= 0) {
field = (uint)list [index].n;
return;
}
}
if (fail != null)
fail.Invoke (name);
}
public void GetField (ref string field, string name, FieldNotFound fail = null)
{
if (type == Type.OBJECT) {
int index = keys.IndexOf (name);
if (index >= 0) {
field = list [index].str;
return;
}
}
if (fail != null)
fail.Invoke (name);
}
public void GetField (string name, GetFieldResponse response, FieldNotFound fail = null)
{
if (response != null && type == Type.OBJECT) {
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 (type == Type.OBJECT)
for (int i = 0; i < keys.Count; i++)
if (keys [i] == name)
return list [i];
return null;
}
public bool HasFields (string[] names)
{
for (int i = 0; i < names.Length; i++)
if (!keys.Contains (names [i]))
return false;
return true;
}
public bool HasField (string name)
{
if (type == Type.OBJECT)
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;
}
/// <summary>
/// Copy a JSONObject. This could probably work better
/// </summary>
/// <returns></returns>
public JSONObject Copy ()
{
return Create (Print ());
}
/*
* The Merge function is experimental. Use at your own risk.
*/
public void Merge (JSONObject obj)
{
MergeRecur (this, obj);
}
/// <summary>
/// Merge object right into left recursively
/// </summary>
/// <param name="left">The left (base) object</param>
/// <param name="right">The right (new) object</param>
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<string> 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 USEFLOAT
if (float.IsInfinity (f))
builder.Append (INFINITY);
else if (float.IsNegativeInfinity (f))
builder.Append (NEGINFINITY);
else if (float.IsNaN (f))
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 (isIntNumber ? n.ToString () : f.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:
case Type.LONG:
//#if USEFLOAT
if (float.IsInfinity (f))
builder.Append (INFINITY);
else if (float.IsNegativeInfinity (f))
builder.Append (NEGINFINITY);
else if (float.IsNaN (f))
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 (isIntNumber ? n.ToString () : f.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;
}
}
public JSONObject this [string index] {
get {
return GetField (index);
}
set {
SetField (index, value);
}
}
public override string ToString ()
{
return Print ();
}
public string ToString (bool pretty)
{
return Print (pretty);
}
public Dictionary<string, string> ToDictionary ()
{
if (type == Type.OBJECT) {
Dictionary<string, string> result = new Dictionary<string, string> ();
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.f + "");
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.LogWarning ("Tried to turn non-Object JSONObject into a dictionary");
return null;
}
public static implicit operator bool (JSONObject o)
{
return o != null;
}
public static JSONObject toJSONObject (object o)
{
IList asList;
IDictionary asDict;
string asStr;
//Log.D ("Test,o:{0}", o);
if (o == null) {
return new JSONObject (Type.NULL);
} else if ((asStr = o as string) != null) {
return CreateStringObject (asStr);
} else if (o is bool) {
return new JSONObject ((bool)o);
} else if ((asList = o as IList) != null) {
//Log.D("a4,o:{0},asList{1}",o,asList);
JSONObject jList = new JSONObject (Type.ARRAY);
for (int i = 0; i < asList.Count; i++) {
jList.Add (toJSONObject (asList [i]));
}
//Log.D("jList:{0}",jList);
return jList;
} else if ((asDict = o as IDictionary) != null) {
JSONObject jDict = new JSONObject (Type.OBJECT);
foreach (string key in asDict.Keys) {
jDict.AddField (key, toJSONObject (asDict [key]));
}
return jDict;
} else if (o is char) {
return CreateStringObject (o.ToString ());
} else if (o is float) {
//Log.D("[ float ]v:{0}, type:{1}.", o, o.GetType());
return new JSONObject ((float)o);
} else if (o is int) {
//Log.D("[ int ]v:{0}, type:{1}.", o, o.GetType());
return new JSONObject ((int)o);
} else if (o is long) {
//Log.D("[ long ]v:{0}, float:{2}, type:{1}.", o, o.GetType(), (long)o);
return new JSONObject ((long)o);
}else if(o is double)
{
return new JSONObject((float)(double)o);
}
else {
DebugHelper.LogError(string.Format("[ toJSONObject ]unsupported type:{0}.", o.GetType()));
}
return 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
public float ParseF ()
{
return IsString ? float.Parse (str) : 0f;
}
public static Vector3 ParseVector3FromJson (JSONObject json)
{
try {
return new Vector3 (float.Parse (json [0].str), float.Parse (json [1].str), float.Parse (json [2].str));
} catch {
return Vector3.zero;
}
}
public static Vector3 ParseVector2FromJson (JSONObject json)
{
try {
return new Vector2 (float.Parse (json [0].str), float.Parse (json [1].str));
} catch {
return Vector2.zero;
}
}
public static float[] ParseFloatArrayFromJson (JSONObject json)
{
try {
float[] result = new float[json.Count];
for (int i = 0; i < result.Length; i++) {
result [i] = float.Parse (json [i].str);
}
return result;
} catch {
return new float[0];
}
}
}