JSONObject.cs 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. #define PRETTY //Comment out when you no longer need to read JSON to disable pretty Print system-wide
  2. //Using doubles will cause errors in VectorTemplates.cs; Unity speaks floats
  3. #define USEFLOAT //Use floats for numbers instead of doubles (enable if you're getting too many significant digits in string output)
  4. //#define POOLING //Currently using a build setting for this one (also it's experimental)
  5. using System.Diagnostics;
  6. using UnityEngine;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. using Debug = UnityEngine.Debug;
  11. /*
  12. * http://www.opensource.org/licenses/lgpl-2.1.php
  13. * JSONObject class
  14. * for use with Unity
  15. * Copyright Matt Schoen 2010 - 2013
  16. */
  17. public class JSONObject
  18. {
  19. #if POOLING
  20. const int MAX_POOL_SIZE = 10000;
  21. public static Queue<JSONObject> releaseQueue = new Queue<JSONObject>();
  22. #endif
  23. const int MAX_DEPTH = 100;
  24. const string INFINITY = "\"INFINITY\"";
  25. const string NEGINFINITY = "\"NEGINFINITY\"";
  26. const string NaN = "\"NaN\"";
  27. static readonly char[] WHITESPACE = new[] { ' ', '\r', '\n', '\t' };
  28. public enum Type
  29. {
  30. NULL,
  31. STRING,
  32. NUMBER,
  33. LONG,
  34. OBJECT,
  35. ARRAY,
  36. BOOL,
  37. BAKED
  38. }
  39. public bool isContainer { get { return (type == Type.ARRAY || type == Type.OBJECT); } }
  40. public Type type = Type.NULL;
  41. public int Count {
  42. get {
  43. if (list == null)
  44. return -1;
  45. return list.Count;
  46. }
  47. }
  48. public List<JSONObject> list;
  49. public List<string> keys;
  50. public string str;
  51. #if USEFLOAT
  52. long nValue;
  53. float fValue;
  54. public long n {
  55. get { return nValue; }
  56. set {
  57. nValue = value;
  58. fValue = nValue;
  59. isIntNumber = true;
  60. }
  61. }
  62. public int i { get { return (int)n; } }
  63. public long l { get { return n; } set { n = value; } }
  64. public float f {
  65. get { return fValue; }
  66. set {
  67. fValue = value;
  68. nValue = (int)fValue;
  69. isIntNumber = false;
  70. }
  71. }
  72. public bool isIntNumber { get; private set; }
  73. #else
  74. public double n;
  75. public float f {
  76. get {
  77. return (float)n;
  78. }
  79. }
  80. #endif
  81. public bool b;
  82. public delegate void AddJSONConents (JSONObject self);
  83. public static JSONObject nullJO { get { return Create (Type.NULL); } }
  84. //an empty, null object
  85. public static JSONObject newObj { get { return Create (Type.OBJECT); } }
  86. //an empty object
  87. public static JSONObject arr { get { return Create (Type.ARRAY); } }
  88. //an empty array
  89. public JSONObject (Type t)
  90. {
  91. type = t;
  92. switch (t) {
  93. case Type.ARRAY:
  94. list = new List<JSONObject> ();
  95. break;
  96. case Type.OBJECT:
  97. list = new List<JSONObject> ();
  98. keys = new List<string> ();
  99. break;
  100. }
  101. }
  102. public JSONObject (bool b)
  103. {
  104. type = Type.BOOL;
  105. this.b = b;
  106. }
  107. public JSONObject (long l)
  108. {
  109. type = Type.LONG;
  110. this.l = l;
  111. }
  112. #if USEFLOAT
  113. public JSONObject (float f)
  114. {
  115. type = Type.NUMBER;
  116. this.f = f;
  117. }
  118. #else
  119. public JSONObject(double d) {
  120. type = Type.NUMBER;
  121. n = d;
  122. }
  123. #endif
  124. public JSONObject (Dictionary<string, string> dic)
  125. {
  126. type = Type.OBJECT;
  127. keys = new List<string> ();
  128. list = new List<JSONObject> ();
  129. //Not sure if it's worth removing the foreach here
  130. foreach (KeyValuePair<string, string> kvp in dic) {
  131. keys.Add (kvp.Key);
  132. list.Add (CreateStringObject (kvp.Value));
  133. }
  134. }
  135. public JSONObject (Dictionary<string, JSONObject> dic)
  136. {
  137. type = Type.OBJECT;
  138. keys = new List<string> ();
  139. list = new List<JSONObject> ();
  140. //Not sure if it's worth removing the foreach here
  141. foreach (KeyValuePair<string, JSONObject> kvp in dic) {
  142. keys.Add (kvp.Key);
  143. list.Add (kvp.Value);
  144. }
  145. }
  146. public JSONObject (AddJSONConents content)
  147. {
  148. content.Invoke (this);
  149. }
  150. public JSONObject (JSONObject[] objs)
  151. {
  152. type = Type.ARRAY;
  153. list = new List<JSONObject> (objs);
  154. }
  155. //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
  156. public static JSONObject StringObject (string val)
  157. {
  158. return CreateStringObject (val);
  159. }
  160. public void Absorb (JSONObject obj)
  161. {
  162. list.AddRange (obj.list);
  163. keys.AddRange (obj.keys);
  164. str = obj.str;
  165. #if USEFLOAT
  166. nValue = obj.nValue;
  167. fValue = obj.fValue;
  168. #else
  169. n = obj.n;
  170. #endif
  171. b = obj.b;
  172. type = obj.type;
  173. }
  174. public static JSONObject Create ()
  175. {
  176. #if POOLING
  177. JSONObject result = null;
  178. while(result == null && releaseQueue.Count > 0) {
  179. result = releaseQueue.Dequeue();
  180. #if DEV
  181. //The following cases should NEVER HAPPEN (but they do...)
  182. if(result == null)
  183. Debug.Log("wtf " + releaseQueue.Count);
  184. else if(result.list != null)
  185. Debug.Log("wtflist " + result.list.Count);
  186. #endif
  187. }
  188. if(result != null)
  189. return result;
  190. #endif
  191. return new JSONObject ();
  192. }
  193. public static JSONObject Create (Type t)
  194. {
  195. JSONObject Obj = Create ();
  196. Obj.type = t;
  197. switch (t) {
  198. case Type.ARRAY:
  199. Obj.list = new List<JSONObject> ();
  200. break;
  201. case Type.OBJECT:
  202. Obj.list = new List<JSONObject> ();
  203. Obj.keys = new List<string> ();
  204. break;
  205. }
  206. return Obj;
  207. }
  208. public static JSONObject Create (bool val)
  209. {
  210. JSONObject Obj = Create ();
  211. Obj.type = Type.BOOL;
  212. Obj.b = val;
  213. return Obj;
  214. }
  215. public static JSONObject Create (float val)
  216. {
  217. JSONObject Obj = Create ();
  218. Obj.type = Type.NUMBER;
  219. #if USEFLOAT
  220. Obj.f = val;
  221. #else
  222. Obj.n = val;
  223. #endif
  224. return Obj;
  225. }
  226. public static JSONObject Create (int val)
  227. {
  228. JSONObject Obj = Create ();
  229. Obj.type = Type.NUMBER;
  230. Obj.n = val;
  231. return Obj;
  232. }
  233. public static JSONObject Create(long val)
  234. {
  235. JSONObject Obj = Create();
  236. Obj.type = Type.LONG;
  237. Obj.l = val;
  238. return Obj;
  239. }
  240. public static JSONObject CreateStringObject (string val)
  241. {
  242. JSONObject Obj = Create ();
  243. Obj.type = Type.STRING;
  244. Obj.str = val;
  245. return Obj;
  246. }
  247. public static JSONObject CreateBakedObject (string val)
  248. {
  249. JSONObject bakedObject = Create ();
  250. bakedObject.type = Type.BAKED;
  251. bakedObject.str = val;
  252. return bakedObject;
  253. }
  254. /// <summary>
  255. /// Create a JSONObject by parsing string data
  256. /// </summary>
  257. /// <param name="val">The string to be parsed</param>
  258. /// <param name="maxDepth">The maximum depth for the parser to search. Set this to to 1 for the first level,
  259. /// 2 for the first 2 levels, etc. It defaults to -2 because -1 is the depth value that is parsed (see below)</param>
  260. /// <param name="storeExcessLevels">Whether to store levels beyond maxDepth in baked JSONObjects</param>
  261. /// <param name="strict">Whether to be strict in the parsing. For example, non-strict parsing will successfully
  262. /// parse "a string" into a string-type </param>
  263. /// <returns></returns>
  264. public static JSONObject Create (string val, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false)
  265. {
  266. JSONObject Obj = Create ();
  267. Obj.Parse (val, maxDepth, storeExcessLevels, strict);
  268. return Obj;
  269. }
  270. public static JSONObject Create (AddJSONConents content)
  271. {
  272. JSONObject Obj = Create ();
  273. content.Invoke (Obj);
  274. return Obj;
  275. }
  276. public static JSONObject Create (Dictionary<string, string> dic)
  277. {
  278. JSONObject Obj = Create ();
  279. Obj.type = Type.OBJECT;
  280. Obj.keys = new List<string> ();
  281. Obj.list = new List<JSONObject> ();
  282. //Not sure if it's worth removing the foreach here
  283. foreach (KeyValuePair<string, string> kvp in dic) {
  284. Obj.keys.Add (kvp.Key);
  285. Obj.list.Add (CreateStringObject (kvp.Value));
  286. }
  287. return Obj;
  288. }
  289. public static JSONObject Create (int[] vals)
  290. {
  291. JSONObject resultObj = Create ();
  292. resultObj.type = Type.ARRAY;
  293. resultObj.list = new List<JSONObject> ();
  294. foreach (int v in vals)
  295. resultObj.list.Add (Create (v));
  296. return resultObj;
  297. }
  298. public static JSONObject CreateArrayJson (params int[] param)
  299. {
  300. return Create (param);
  301. }
  302. public JSONObject ()
  303. {
  304. }
  305. #region PARSE
  306. public JSONObject (string str, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false)
  307. { //create a new JSONObject from a string (this will also create any children, and parse the whole string)
  308. Parse (str, maxDepth, storeExcessLevels, strict);
  309. }
  310. // public static bool output_debug = false;
  311. void Parse (string inStr, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false)
  312. {
  313. if (!string.IsNullOrEmpty (inStr)) {
  314. inStr = inStr.Trim (WHITESPACE);
  315. if (strict) {
  316. if (inStr [0] != '[' && inStr [0] != '{') {
  317. type = Type.NULL;
  318. Debug.LogWarning ("Improper (strict) JSON formatting. First character must be [ or {");
  319. return;
  320. }
  321. }
  322. if (inStr.Length > 0) {
  323. if (string.Compare (inStr, "true", true) == 0) {
  324. type = Type.BOOL;
  325. b = true;
  326. } else if (string.Compare (inStr, "false", true) == 0) {
  327. type = Type.BOOL;
  328. b = false;
  329. } else if (string.Compare (inStr, "null", true) == 0) {
  330. type = Type.NULL;
  331. //#if USEFLOAT
  332. } else if (inStr == INFINITY) {
  333. type = Type.NUMBER;
  334. f = float.PositiveInfinity;
  335. } else if (inStr == NEGINFINITY) {
  336. type = Type.NUMBER;
  337. f = float.NegativeInfinity;
  338. } else if (inStr == NaN) {
  339. type = Type.NUMBER;
  340. f = float.NaN;
  341. //#else
  342. // } else if(str == INFINITY) {
  343. // type = Type.NUMBER;
  344. // n = double.PositiveInfinity;
  345. // } else if(str == NEGINFINITY) {
  346. // type = Type.NUMBER;
  347. // n = double.NegativeInfinity;
  348. // } else if(str == NaN) {
  349. // type = Type.NUMBER;
  350. // n = double.NaN;
  351. //#endif
  352. } else if (inStr [0] == '"') {
  353. type = Type.STRING;
  354. str = inStr.Substring (1, inStr.Length - 2);
  355. if (str.Length > 0 && str.Contains ("\\u")) {
  356. str = UnicodeUtil.Convert (str);
  357. }
  358. } else {
  359. int tokenTmp = 1;
  360. /*
  361. * Checking for the following formatting (www.json.org)
  362. * object - {"field1":value,"field2":value}
  363. * array - [value,value,value]
  364. * value - string - "string"
  365. * - number - 0.0
  366. * - bool - true -or- false
  367. * - null - null
  368. */
  369. int offset = 0;
  370. switch (inStr [offset]) {
  371. case '{':
  372. type = Type.OBJECT;
  373. keys = new List<string> ();
  374. list = new List<JSONObject> ();
  375. break;
  376. case '[':
  377. type = Type.ARRAY;
  378. list = new List<JSONObject> ();
  379. break;
  380. default:
  381. try {
  382. #if USEFLOAT
  383. if (inStr.IndexOf ('.') >= 0 || inStr.IndexOf ('e') >= 0 || inStr.IndexOf ('E') >= 0) {
  384. f = System.Convert.ToSingle (inStr);
  385. } else {
  386. n = System.Convert.ToInt64 (inStr);
  387. }
  388. #else
  389. n = System.Convert.ToDouble(str);
  390. #endif
  391. type = Type.NUMBER;
  392. } catch (System.FormatException) {
  393. type = Type.NULL;
  394. //Debug.LogWarning ("improper JSON formatting:" + str);
  395. }
  396. return;
  397. }
  398. string propName = "";
  399. bool openQuote = false;
  400. bool inProp = false;
  401. int depth = 0;
  402. while (++offset < inStr.Length) {
  403. if (System.Array.IndexOf (WHITESPACE, inStr [offset]) > -1)
  404. continue;
  405. if (inStr [offset] == '\\')
  406. offset += 2;
  407. if (inStr [offset] == '"') {
  408. if (openQuote) {
  409. if (!inProp && depth == 0 && type == Type.OBJECT)
  410. propName = inStr.Substring (tokenTmp + 1, offset - tokenTmp - 1);
  411. openQuote = false;
  412. } else {
  413. if (depth == 0 && type == Type.OBJECT)
  414. tokenTmp = offset;
  415. openQuote = true;
  416. }
  417. }
  418. if (openQuote)
  419. continue;
  420. if (type == Type.OBJECT && depth == 0) {
  421. if (inStr [offset] == ':') {
  422. tokenTmp = offset + 1;
  423. inProp = true;
  424. }
  425. }
  426. if (inStr [offset] == '[' || inStr [offset] == '{') {
  427. depth++;
  428. } else if (inStr [offset] == ']' || inStr [offset] == '}') {
  429. depth--;
  430. }
  431. //if (encounter a ',' at top level) || a closing ]/}
  432. if ((inStr [offset] == ',' && depth == 0) || depth < 0) {
  433. inProp = false;
  434. string inner = inStr.Substring (tokenTmp, offset - tokenTmp).Trim (WHITESPACE);
  435. if (inner.Length > 0) {
  436. // if (output_debug) {
  437. // Log.D ("{0}", inner);
  438. // if (inner.CompareTo ("1442809048") == 0)
  439. // Log.D ("{0}", inner);
  440. // }
  441. if (type == Type.OBJECT)
  442. keys.Add (propName);
  443. if (maxDepth != -1) //maxDepth of -1 is the end of the line
  444. list.Add (Create (inner, (maxDepth < -1) ? -2 : maxDepth - 1));
  445. else if (storeExcessLevels)
  446. list.Add (CreateBakedObject (inner));
  447. }
  448. tokenTmp = offset + 1;
  449. }
  450. }
  451. }
  452. } else
  453. type = Type.NULL;
  454. } else
  455. type = Type.NULL; //If the string is missing, this is a null
  456. //Profiler.EndSample();
  457. }
  458. #endregion
  459. public bool IsNumber { get { return type == Type.NUMBER; } }
  460. public bool IsNull { get { return type == Type.NULL; } }
  461. public bool IsString { get { return type == Type.STRING; } }
  462. public bool IsBool { get { return type == Type.BOOL; } }
  463. public bool IsArray { get { return type == Type.ARRAY; } }
  464. public bool IsObject { get { return type == Type.OBJECT; } }
  465. public void Add (bool val)
  466. {
  467. Add (Create (val));
  468. }
  469. public void Add (float val)
  470. {
  471. Add (Create (val));
  472. }
  473. public void Add(long val)
  474. {
  475. Add (Create(val));
  476. }
  477. public void Add (int val)
  478. {
  479. Add (Create (val));
  480. }
  481. public void Add (string str)
  482. {
  483. Add (CreateStringObject (str));
  484. }
  485. public void Add (AddJSONConents content)
  486. {
  487. Add (Create (content));
  488. }
  489. public void Add (JSONObject obj)
  490. {
  491. if (obj) { //Don't do anything if the object is null
  492. if (type != Type.ARRAY) {
  493. type = Type.ARRAY; //Congratulations, son, you're an ARRAY now
  494. if (list == null)
  495. list = new List<JSONObject> ();
  496. }
  497. list.Add (obj);
  498. }
  499. }
  500. public void AddBoolField (string name, bool val)
  501. {
  502. AddField (name, Create (val));
  503. }
  504. public void AddField (string name, bool val)
  505. {
  506. AddField (name, Create (val));
  507. }
  508. public void AddField (string name, float val)
  509. {
  510. AddField (name, Create (val));
  511. }
  512. public void AddField (string name, int val)
  513. {
  514. AddField (name, Create (val));
  515. }
  516. public void AddField(string name, long val)
  517. {
  518. AddField(name, Create(val));
  519. }
  520. public void AddField (string name, AddJSONConents content)
  521. {
  522. AddField (name, Create (content));
  523. }
  524. public void AddField (string name, string val)
  525. {
  526. AddField (name, CreateStringObject (val));
  527. }
  528. public void AddField (string name, JSONObject obj)
  529. {
  530. if (obj) { //Don't do anything if the object is null
  531. if (type != Type.OBJECT) {
  532. if (keys == null)
  533. keys = new List<string> ();
  534. if (type == Type.ARRAY) {
  535. for (int i = 0; i < list.Count; i++)
  536. keys.Add (i + "");
  537. } else if (list == null)
  538. list = new List<JSONObject> ();
  539. type = Type.OBJECT; //Congratulations, son, you're an OBJECT now
  540. }
  541. keys.Add (name);
  542. list.Add (obj);
  543. }
  544. }
  545. public void SetField (string name, bool val)
  546. {
  547. SetField (name, Create (val));
  548. }
  549. public void SetField (string name, float val)
  550. {
  551. SetField (name, Create (val));
  552. }
  553. public void SetField (string name, int val)
  554. {
  555. SetField (name, Create (val));
  556. }
  557. public void SetField (string name, JSONObject obj)
  558. {
  559. if (HasField (name)) {
  560. list.Remove (this [name]);
  561. keys.Remove (name);
  562. }
  563. AddField (name, obj);
  564. }
  565. public void RemoveField (string name)
  566. {
  567. if (keys.IndexOf (name) > -1) {
  568. list.RemoveAt (keys.IndexOf (name));
  569. keys.Remove (name);
  570. }
  571. }
  572. public delegate void FieldNotFound (string name);
  573. public delegate void GetFieldResponse (JSONObject obj);
  574. public void GetField (ref bool field, string name, FieldNotFound fail = null)
  575. {
  576. if (type == Type.OBJECT) {
  577. int index = keys.IndexOf (name);
  578. if (index >= 0) {
  579. field = list [index].b;
  580. return;
  581. }
  582. }
  583. if (fail != null)
  584. fail.Invoke (name);
  585. }
  586. //#if USEFLOAT
  587. public void GetField (ref float field, string name, FieldNotFound fail = null)
  588. {
  589. //#else
  590. //public void GetField(ref double field, string name, FieldNotFound fail = null) {
  591. //#endif
  592. if (type == Type.OBJECT) {
  593. int index = keys.IndexOf (name);
  594. if (index >= 0) {
  595. field = list [index].f;
  596. return;
  597. }
  598. }
  599. if (fail != null)
  600. fail.Invoke (name);
  601. }
  602. public void GetField (ref int field, string name, FieldNotFound fail = null)
  603. {
  604. if (type == Type.OBJECT) {
  605. int index = keys.IndexOf (name);
  606. if (index >= 0) {
  607. field = (int)list [index].n;
  608. return;
  609. }
  610. }
  611. if (fail != null)
  612. fail.Invoke (name);
  613. }
  614. public void GetField (ref uint field, string name, FieldNotFound fail = null)
  615. {
  616. if (type == Type.OBJECT) {
  617. int index = keys.IndexOf (name);
  618. if (index >= 0) {
  619. field = (uint)list [index].n;
  620. return;
  621. }
  622. }
  623. if (fail != null)
  624. fail.Invoke (name);
  625. }
  626. public void GetField (ref string field, string name, FieldNotFound fail = null)
  627. {
  628. if (type == Type.OBJECT) {
  629. int index = keys.IndexOf (name);
  630. if (index >= 0) {
  631. field = list [index].str;
  632. return;
  633. }
  634. }
  635. if (fail != null)
  636. fail.Invoke (name);
  637. }
  638. public void GetField (string name, GetFieldResponse response, FieldNotFound fail = null)
  639. {
  640. if (response != null && type == Type.OBJECT) {
  641. int index = keys.IndexOf (name);
  642. if (index >= 0) {
  643. response.Invoke (list [index]);
  644. return;
  645. }
  646. }
  647. if (fail != null)
  648. fail.Invoke (name);
  649. }
  650. public JSONObject GetField (string name)
  651. {
  652. if (type == Type.OBJECT)
  653. for (int i = 0; i < keys.Count; i++)
  654. if (keys [i] == name)
  655. return list [i];
  656. return null;
  657. }
  658. public bool HasFields (string[] names)
  659. {
  660. for (int i = 0; i < names.Length; i++)
  661. if (!keys.Contains (names [i]))
  662. return false;
  663. return true;
  664. }
  665. public bool HasField (string name)
  666. {
  667. if (type == Type.OBJECT)
  668. for (int i = 0; i < keys.Count; i++)
  669. if (keys [i] == name)
  670. return true;
  671. return false;
  672. }
  673. public void Clear ()
  674. {
  675. type = Type.NULL;
  676. if (list != null)
  677. list.Clear ();
  678. if (keys != null)
  679. keys.Clear ();
  680. str = "";
  681. n = 0;
  682. b = false;
  683. }
  684. /// <summary>
  685. /// Copy a JSONObject. This could probably work better
  686. /// </summary>
  687. /// <returns></returns>
  688. public JSONObject Copy ()
  689. {
  690. return Create (Print ());
  691. }
  692. /*
  693. * The Merge function is experimental. Use at your own risk.
  694. */
  695. public void Merge (JSONObject obj)
  696. {
  697. MergeRecur (this, obj);
  698. }
  699. /// <summary>
  700. /// Merge object right into left recursively
  701. /// </summary>
  702. /// <param name="left">The left (base) object</param>
  703. /// <param name="right">The right (new) object</param>
  704. static void MergeRecur (JSONObject left, JSONObject right)
  705. {
  706. if (left.type == Type.NULL)
  707. left.Absorb (right);
  708. else if (left.type == Type.OBJECT && right.type == Type.OBJECT) {
  709. for (int i = 0; i < right.list.Count; i++) {
  710. string key = right.keys [i];
  711. if (right [i].isContainer) {
  712. if (left.HasField (key))
  713. MergeRecur (left [key], right [i]);
  714. else
  715. left.AddField (key, right [i]);
  716. } else {
  717. if (left.HasField (key))
  718. left.SetField (key, right [i]);
  719. else
  720. left.AddField (key, right [i]);
  721. }
  722. }
  723. } else if (left.type == Type.ARRAY && right.type == Type.ARRAY) {
  724. if (right.Count > left.Count) {
  725. Debug.LogError ("Cannot merge arrays when right object has more elements");
  726. return;
  727. }
  728. for (int i = 0; i < right.list.Count; i++) {
  729. if (left [i].type == right [i].type) { //Only overwrite with the same type
  730. if (left [i].isContainer)
  731. MergeRecur (left [i], right [i]);
  732. else {
  733. left [i] = right [i];
  734. }
  735. }
  736. }
  737. }
  738. }
  739. public void Bake ()
  740. {
  741. if (type != Type.BAKED) {
  742. str = Print ();
  743. type = Type.BAKED;
  744. }
  745. }
  746. public IEnumerable BakeAsync ()
  747. {
  748. if (type != Type.BAKED) {
  749. foreach (string s in PrintAsync()) {
  750. if (s == null)
  751. yield return s;
  752. else {
  753. str = s;
  754. }
  755. }
  756. type = Type.BAKED;
  757. }
  758. }
  759. #pragma warning disable 219
  760. public string Print (bool pretty = false)
  761. {
  762. StringBuilder builder = new StringBuilder ();
  763. Stringify (0, builder, pretty);
  764. return builder.ToString ();
  765. }
  766. public IEnumerable<string> PrintAsync (bool pretty = false)
  767. {
  768. StringBuilder builder = new StringBuilder ();
  769. printWatch.Reset ();
  770. printWatch.Start ();
  771. foreach (IEnumerable e in StringifyAsync(0, builder, pretty)) {
  772. yield return null;
  773. }
  774. yield return builder.ToString ();
  775. }
  776. #pragma warning restore 219
  777. #region STRINGIFY
  778. const float maxFrameTime = 0.008f;
  779. static readonly Stopwatch printWatch = new Stopwatch ();
  780. IEnumerable StringifyAsync (int depth, StringBuilder builder, bool pretty = false)
  781. { //Convert the JSONObject into a string
  782. //Profiler.BeginSample("JSONprint");
  783. if (depth++ > MAX_DEPTH) {
  784. Debug.Log ("reached max depth!");
  785. yield break;
  786. }
  787. if (printWatch.Elapsed.TotalSeconds > maxFrameTime) {
  788. printWatch.Reset ();
  789. yield return null;
  790. printWatch.Start ();
  791. }
  792. switch (type) {
  793. case Type.BAKED:
  794. builder.Append (str);
  795. break;
  796. case Type.STRING:
  797. builder.AppendFormat ("\"{0}\"", str);
  798. break;
  799. case Type.NUMBER:
  800. //#if USEFLOAT
  801. if (float.IsInfinity (f))
  802. builder.Append (INFINITY);
  803. else if (float.IsNegativeInfinity (f))
  804. builder.Append (NEGINFINITY);
  805. else if (float.IsNaN (f))
  806. builder.Append (NaN);
  807. //#else
  808. // if(double.IsInfinity(n))
  809. // builder.Append(INFINITY);
  810. // else if(double.IsNegativeInfinity(n))
  811. // builder.Append(NEGINFINITY);
  812. // else if(double.IsNaN(n))
  813. // builder.Append(NaN);
  814. //#endif
  815. else {
  816. builder.Append (isIntNumber ? n.ToString () : f.ToString ());
  817. }
  818. break;
  819. case Type.OBJECT:
  820. builder.Append ("{");
  821. if (list.Count > 0) {
  822. #if(PRETTY) //for a bit more readability, comment the define above to disable system-wide
  823. if (pretty)
  824. builder.Append ("\n");
  825. #endif
  826. for (int i = 0; i < list.Count; i++) {
  827. string key = keys [i];
  828. JSONObject obj = list [i];
  829. if (obj) {
  830. #if(PRETTY)
  831. if (pretty)
  832. for (int j = 0; j < depth; j++)
  833. builder.Append ("\t"); //for a bit more readability
  834. #endif
  835. builder.AppendFormat ("\"{0}\":", key);
  836. foreach (IEnumerable e in obj.StringifyAsync(depth, builder, pretty))
  837. yield return e;
  838. builder.Append (",");
  839. #if(PRETTY)
  840. if (pretty)
  841. builder.Append ("\n");
  842. #endif
  843. }
  844. }
  845. //#if(PRETTY)
  846. if (pretty)
  847. builder.Length -= 2;
  848. else
  849. //#endif
  850. builder.Length--;
  851. }
  852. #if(PRETTY)
  853. if (pretty && list.Count > 0) {
  854. builder.Append ("\n");
  855. for (int j = 0; j < depth - 1; j++)
  856. builder.Append ("\t"); //for a bit more readability
  857. }
  858. #endif
  859. builder.Append ("}");
  860. break;
  861. case Type.ARRAY:
  862. builder.Append ("[");
  863. if (list.Count > 0) {
  864. #if(PRETTY)
  865. if (pretty)
  866. builder.Append ("\n"); //for a bit more readability
  867. #endif
  868. for (int i = 0; i < list.Count; i++) {
  869. if (list [i]) {
  870. #if(PRETTY)
  871. if (pretty)
  872. for (int j = 0; j < depth; j++)
  873. builder.Append ("\t"); //for a bit more readability
  874. #endif
  875. foreach (IEnumerable e in list[i].StringifyAsync(depth, builder, pretty))
  876. yield return e;
  877. builder.Append (",");
  878. #if(PRETTY)
  879. if (pretty)
  880. builder.Append ("\n"); //for a bit more readability
  881. #endif
  882. }
  883. }
  884. //#if(PRETTY)
  885. if (pretty)
  886. builder.Length -= 2;
  887. else
  888. //#endif
  889. builder.Length--;
  890. }
  891. #if(PRETTY)
  892. if (pretty && list.Count > 0) {
  893. builder.Append ("\n");
  894. for (int j = 0; j < depth - 1; j++)
  895. builder.Append ("\t"); //for a bit more readability
  896. }
  897. #endif
  898. builder.Append ("]");
  899. break;
  900. case Type.BOOL:
  901. if (b)
  902. builder.Append ("true");
  903. else
  904. builder.Append ("false");
  905. break;
  906. case Type.NULL:
  907. builder.Append ("null");
  908. break;
  909. }
  910. //Profiler.EndSample();
  911. }
  912. //TODO: Refactor Stringify functions to share core logic
  913. /*
  914. * I know, I know, this is really bad form. It turns out that there is a
  915. * significant amount of garbage created when calling as a coroutine, so this
  916. * method is duplicated. Hopefully there won't be too many future changes, but
  917. * I would still like a more elegant way to optionaly yield
  918. */
  919. void Stringify (int depth, StringBuilder builder, bool pretty = false)
  920. { //Convert the JSONObject into a string
  921. //Profiler.BeginSample("JSONprint");
  922. if (depth++ > MAX_DEPTH) {
  923. Debug.Log ("reached max depth!");
  924. return;
  925. }
  926. switch (type) {
  927. case Type.BAKED:
  928. builder.Append (str);
  929. break;
  930. case Type.STRING:
  931. builder.AppendFormat ("\"{0}\"", str);
  932. break;
  933. case Type.NUMBER:
  934. case Type.LONG:
  935. //#if USEFLOAT
  936. if (float.IsInfinity (f))
  937. builder.Append (INFINITY);
  938. else if (float.IsNegativeInfinity (f))
  939. builder.Append (NEGINFINITY);
  940. else if (float.IsNaN (f))
  941. builder.Append (NaN);
  942. //#else
  943. // if(double.IsInfinity(n))
  944. // builder.Append(INFINITY);
  945. // else if(double.IsNegativeInfinity(n))
  946. // builder.Append(NEGINFINITY);
  947. // else if(double.IsNaN(n))
  948. // builder.Append(NaN);
  949. //#endif
  950. else
  951. builder.Append (isIntNumber ? n.ToString () : f.ToString ());
  952. break;
  953. case Type.OBJECT:
  954. builder.Append ("{");
  955. if (list.Count > 0) {
  956. #if(PRETTY) //for a bit more readability, comment the define above to disable system-wide
  957. if (pretty)
  958. builder.Append ("\n");
  959. #endif
  960. for (int i = 0; i < list.Count; i++) {
  961. string key = keys [i];
  962. JSONObject obj = list [i];
  963. if (obj) {
  964. #if(PRETTY)
  965. if (pretty)
  966. for (int j = 0; j < depth; j++)
  967. builder.Append ("\t"); //for a bit more readability
  968. #endif
  969. builder.AppendFormat ("\"{0}\":", key);
  970. obj.Stringify (depth, builder, pretty);
  971. builder.Append (",");
  972. #if(PRETTY)
  973. if (pretty)
  974. builder.Append ("\n");
  975. #endif
  976. }
  977. }
  978. //#if(PRETTY)
  979. if (pretty)
  980. builder.Length -= 2;
  981. else
  982. //#endif
  983. builder.Length--;
  984. }
  985. #if(PRETTY)
  986. if (pretty && list.Count > 0) {
  987. builder.Append ("\n");
  988. for (int j = 0; j < depth - 1; j++)
  989. builder.Append ("\t"); //for a bit more readability
  990. }
  991. #endif
  992. builder.Append ("}");
  993. break;
  994. case Type.ARRAY:
  995. builder.Append ("[");
  996. if (list.Count > 0) {
  997. #if(PRETTY)
  998. if (pretty)
  999. builder.Append ("\n"); //for a bit more readability
  1000. #endif
  1001. for (int i = 0; i < list.Count; i++) {
  1002. if (list [i]) {
  1003. #if(PRETTY)
  1004. if (pretty)
  1005. for (int j = 0; j < depth; j++)
  1006. builder.Append ("\t"); //for a bit more readability
  1007. #endif
  1008. list [i].Stringify (depth, builder, pretty);
  1009. builder.Append (",");
  1010. #if(PRETTY)
  1011. if (pretty)
  1012. builder.Append ("\n"); //for a bit more readability
  1013. #endif
  1014. }
  1015. }
  1016. //#if(PRETTY)
  1017. if (pretty)
  1018. builder.Length -= 2;
  1019. else
  1020. //#endif
  1021. builder.Length--;
  1022. }
  1023. #if(PRETTY)
  1024. if (pretty && list.Count > 0) {
  1025. builder.Append ("\n");
  1026. for (int j = 0; j < depth - 1; j++)
  1027. builder.Append ("\t"); //for a bit more readability
  1028. }
  1029. #endif
  1030. builder.Append ("]");
  1031. break;
  1032. case Type.BOOL:
  1033. if (b)
  1034. builder.Append ("true");
  1035. else
  1036. builder.Append ("false");
  1037. break;
  1038. case Type.NULL:
  1039. builder.Append ("null");
  1040. break;
  1041. }
  1042. //Profiler.EndSample();
  1043. }
  1044. #endregion
  1045. public static implicit operator WWWForm (JSONObject obj)
  1046. {
  1047. WWWForm form = new WWWForm ();
  1048. for (int i = 0; i < obj.list.Count; i++) {
  1049. string key = i + "";
  1050. if (obj.type == Type.OBJECT)
  1051. key = obj.keys [i];
  1052. string val = obj.list [i].ToString ();
  1053. if (obj.list [i].type == Type.STRING)
  1054. val = val.Replace ("\"", "");
  1055. form.AddField (key, val);
  1056. }
  1057. return form;
  1058. }
  1059. public JSONObject this [int index] {
  1060. get {
  1061. if (list.Count > index)
  1062. return list [index];
  1063. return null;
  1064. }
  1065. set {
  1066. if (list.Count > index)
  1067. list [index] = value;
  1068. }
  1069. }
  1070. public JSONObject this [string index] {
  1071. get {
  1072. return GetField (index);
  1073. }
  1074. set {
  1075. SetField (index, value);
  1076. }
  1077. }
  1078. public override string ToString ()
  1079. {
  1080. return Print ();
  1081. }
  1082. public string ToString (bool pretty)
  1083. {
  1084. return Print (pretty);
  1085. }
  1086. public Dictionary<string, string> ToDictionary ()
  1087. {
  1088. if (type == Type.OBJECT) {
  1089. Dictionary<string, string> result = new Dictionary<string, string> ();
  1090. for (int i = 0; i < list.Count; i++) {
  1091. JSONObject val = list [i];
  1092. switch (val.type) {
  1093. case Type.STRING:
  1094. result.Add (keys [i], val.str);
  1095. break;
  1096. case Type.NUMBER:
  1097. result.Add (keys [i], val.f + "");
  1098. break;
  1099. case Type.BOOL:
  1100. result.Add (keys [i], val.b + "");
  1101. break;
  1102. default:
  1103. Debug.LogWarning ("Omitting object: " + keys [i] + " in dictionary conversion");
  1104. break;
  1105. }
  1106. }
  1107. return result;
  1108. }
  1109. Debug.LogWarning ("Tried to turn non-Object JSONObject into a dictionary");
  1110. return null;
  1111. }
  1112. public static implicit operator bool (JSONObject o)
  1113. {
  1114. return o != null;
  1115. }
  1116. public static JSONObject toJSONObject (object o)
  1117. {
  1118. IList asList;
  1119. IDictionary asDict;
  1120. string asStr;
  1121. //Log.D ("Test,o:{0}", o);
  1122. if (o == null) {
  1123. return new JSONObject (Type.NULL);
  1124. } else if ((asStr = o as string) != null) {
  1125. return CreateStringObject (asStr);
  1126. } else if (o is bool) {
  1127. return new JSONObject ((bool)o);
  1128. } else if ((asList = o as IList) != null) {
  1129. //Log.D("a4,o:{0},asList{1}",o,asList);
  1130. JSONObject jList = new JSONObject (Type.ARRAY);
  1131. for (int i = 0; i < asList.Count; i++) {
  1132. jList.Add (toJSONObject (asList [i]));
  1133. }
  1134. //Log.D("jList:{0}",jList);
  1135. return jList;
  1136. } else if ((asDict = o as IDictionary) != null) {
  1137. JSONObject jDict = new JSONObject (Type.OBJECT);
  1138. foreach (string key in asDict.Keys) {
  1139. jDict.AddField (key, toJSONObject (asDict [key]));
  1140. }
  1141. return jDict;
  1142. } else if (o is char) {
  1143. return CreateStringObject (o.ToString ());
  1144. } else if (o is float) {
  1145. //Log.D("[ float ]v:{0}, type:{1}.", o, o.GetType());
  1146. return new JSONObject ((float)o);
  1147. } else if (o is int) {
  1148. //Log.D("[ int ]v:{0}, type:{1}.", o, o.GetType());
  1149. return new JSONObject ((int)o);
  1150. } else if (o is long) {
  1151. //Log.D("[ long ]v:{0}, float:{2}, type:{1}.", o, o.GetType(), (long)o);
  1152. return new JSONObject ((long)o);
  1153. }else if(o is double)
  1154. {
  1155. return new JSONObject((float)(double)o);
  1156. }
  1157. else {
  1158. DebugHelper.LogError(string.Format("[ toJSONObject ]unsupported type:{0}.", o.GetType()));
  1159. }
  1160. return null;
  1161. }
  1162. #if POOLING
  1163. static bool pool = true;
  1164. public static void ClearPool() {
  1165. pool = false;
  1166. releaseQueue.Clear();
  1167. pool = true;
  1168. }
  1169. ~JSONObject() {
  1170. if(pool && releaseQueue.Count < MAX_POOL_SIZE) {
  1171. type = Type.NULL;
  1172. list = null;
  1173. keys = null;
  1174. str = "";
  1175. n = 0;
  1176. b = false;
  1177. releaseQueue.Enqueue(this);
  1178. }
  1179. }
  1180. #endif
  1181. public float ParseF ()
  1182. {
  1183. return IsString ? float.Parse (str) : 0f;
  1184. }
  1185. public static Vector3 ParseVector3FromJson (JSONObject json)
  1186. {
  1187. try {
  1188. return new Vector3 (float.Parse (json [0].str), float.Parse (json [1].str), float.Parse (json [2].str));
  1189. } catch {
  1190. return Vector3.zero;
  1191. }
  1192. }
  1193. public static Vector3 ParseVector2FromJson (JSONObject json)
  1194. {
  1195. try {
  1196. return new Vector2 (float.Parse (json [0].str), float.Parse (json [1].str));
  1197. } catch {
  1198. return Vector2.zero;
  1199. }
  1200. }
  1201. public static float[] ParseFloatArrayFromJson (JSONObject json)
  1202. {
  1203. try {
  1204. float[] result = new float[json.Count];
  1205. for (int i = 0; i < result.Length; i++) {
  1206. result [i] = float.Parse (json [i].str);
  1207. }
  1208. return result;
  1209. } catch {
  1210. return new float[0];
  1211. }
  1212. }
  1213. }