PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/unity/Assets/Futile/ThirdParty/MiniJson.cs

https://bitbucket.org/ironpencil/tankfight
C# | 569 lines | 443 code | 44 blank | 82 comment | 29 complexity | 11d9c38476a5136764f89c4f28832ce4 MD5 | raw file
  1. /*
  2. * Copyright (c) 2012 Calvin Rien
  3. *
  4. * Based on the JSON parser by Patrick van Bergen
  5. * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
  6. *
  7. * Simplified it so that it doesn't throw exceptions
  8. * and can be used in Unity iPhone with maximum code stripping.
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining
  11. * a copy of this software and associated documentation files (the
  12. * "Software"), to deal in the Software without restriction, including
  13. * without limitation the rights to use, copy, modify, merge, publish,
  14. * distribute, sublicense, and/or sell copies of the Software, and to
  15. * permit persons to whom the Software is furnished to do so, subject to
  16. * the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be
  19. * included in all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  24. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  25. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  26. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  27. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. */
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.IO;
  33. using System.Text;
  34. // Example usage:
  35. //
  36. // using UnityEngine;
  37. // using System.Collections;
  38. // using System.Collections.Generic;
  39. // using MiniJson;
  40. //
  41. // public class MiniJsonTest : MonoBehaviour {
  42. // void Start () {
  43. // var jsonString = "{ \"array\": [1.44,2,3], " +
  44. // "\"object\": {\"key1\":\"value1\", \"key2\":256}, " +
  45. // "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " +
  46. // "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " +
  47. // "\"int\": 65536, " +
  48. // "\"float\": 3.1415926, " +
  49. // "\"bool\": true, " +
  50. // "\"null\": null }";
  51. //
  52. // var dict = Json.Deserialize(jsonString) as Dictionary<string,object>;
  53. //
  54. // Debug.Log("deserialized: " + dict.GetType());
  55. // Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]);
  56. // Debug.Log("dict['string']: " + (string) dict["string"]);
  57. // Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles
  58. // Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs
  59. // Debug.Log("dict['unicode']: " + (string) dict["unicode"]);
  60. //
  61. // var str = Json.Serialize(dict);
  62. //
  63. // Debug.Log("serialized: " + str);
  64. // }
  65. // }
  66. /// <summary>
  67. /// This class encodes and decodes JSON strings.
  68. /// Spec. details, see http://www.json.org/
  69. ///
  70. /// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary.
  71. /// All numbers are parsed to doubles.
  72. /// </summary>
  73. public static class Json {
  74. /// <summary>
  75. /// Parses the string json into a value
  76. /// </summary>
  77. /// <param name="json">A JSON string.</param>
  78. /// <returns>An List&lt;object&gt;, a Dictionary&lt;string, object&gt;, a double, an integer,a string, null, true, or false</returns>
  79. public static object Deserialize(string json) {
  80. // save the string for debug information
  81. if (json == null) {
  82. return null;
  83. }
  84. return Parser.Parse(json);
  85. }
  86. sealed class Parser : IDisposable {
  87. const string WHITE_SPACE = " \t\n\r";
  88. const string WORD_BREAK = " \t\n\r{}[],:\"";
  89. enum TOKEN {
  90. NONE,
  91. CURLY_OPEN,
  92. CURLY_CLOSE,
  93. SQUARED_OPEN,
  94. SQUARED_CLOSE,
  95. COLON,
  96. COMMA,
  97. STRING,
  98. NUMBER,
  99. TRUE,
  100. FALSE,
  101. NULL
  102. };
  103. StringReader json;
  104. Parser(string jsonString) {
  105. json = new StringReader(jsonString);
  106. }
  107. public static object Parse(string jsonString) {
  108. using (var instance = new Parser(jsonString)) {
  109. return instance.ParseValue();
  110. }
  111. }
  112. public void Dispose() {
  113. json.Dispose();
  114. json = null;
  115. }
  116. Dictionary<string, object> ParseObject() {
  117. Dictionary<string, object> table = new Dictionary<string, object>();
  118. // ditch opening brace
  119. json.Read();
  120. // {
  121. while (true) {
  122. switch (NextToken) {
  123. case TOKEN.NONE:
  124. return null;
  125. case TOKEN.COMMA:
  126. continue;
  127. case TOKEN.CURLY_CLOSE:
  128. return table;
  129. default:
  130. // name
  131. string name = ParseString();
  132. if (name == null) {
  133. return null;
  134. }
  135. // :
  136. if (NextToken != TOKEN.COLON) {
  137. return null;
  138. }
  139. // ditch the colon
  140. json.Read();
  141. // value
  142. table[name] = ParseValue();
  143. break;
  144. }
  145. }
  146. }
  147. List<object> ParseArray() {
  148. List<object> array = new List<object>();
  149. // ditch opening bracket
  150. json.Read();
  151. // [
  152. var parsing = true;
  153. while (parsing) {
  154. TOKEN nextToken = NextToken;
  155. switch (nextToken) {
  156. case TOKEN.NONE:
  157. return null;
  158. case TOKEN.COMMA:
  159. continue;
  160. case TOKEN.SQUARED_CLOSE:
  161. parsing = false;
  162. break;
  163. default:
  164. object value = ParseByToken(nextToken);
  165. array.Add(value);
  166. break;
  167. }
  168. }
  169. return array;
  170. }
  171. object ParseValue() {
  172. TOKEN nextToken = NextToken;
  173. return ParseByToken(nextToken);
  174. }
  175. object ParseByToken(TOKEN token) {
  176. switch (token) {
  177. case TOKEN.STRING:
  178. return ParseString();
  179. case TOKEN.NUMBER:
  180. return ParseNumber();
  181. case TOKEN.CURLY_OPEN:
  182. return ParseObject();
  183. case TOKEN.SQUARED_OPEN:
  184. return ParseArray();
  185. case TOKEN.TRUE:
  186. return true;
  187. case TOKEN.FALSE:
  188. return false;
  189. case TOKEN.NULL:
  190. return null;
  191. default:
  192. return null;
  193. }
  194. }
  195. string ParseString() {
  196. StringBuilder s = new StringBuilder();
  197. char c;
  198. // ditch opening quote
  199. json.Read();
  200. bool parsing = true;
  201. while (parsing) {
  202. if (json.Peek() == -1) {
  203. parsing = false;
  204. break;
  205. }
  206. c = NextChar;
  207. switch (c) {
  208. case '"':
  209. parsing = false;
  210. break;
  211. case '\\':
  212. if (json.Peek() == -1) {
  213. parsing = false;
  214. break;
  215. }
  216. c = NextChar;
  217. switch (c) {
  218. case '"':
  219. case '\\':
  220. case '/':
  221. s.Append(c);
  222. break;
  223. case 'b':
  224. s.Append('\b');
  225. break;
  226. case 'f':
  227. s.Append('\f');
  228. break;
  229. case 'n':
  230. s.Append('\n');
  231. break;
  232. case 'r':
  233. s.Append('\r');
  234. break;
  235. case 't':
  236. s.Append('\t');
  237. break;
  238. case 'u':
  239. var hex = new StringBuilder();
  240. for (int i=0; i< 4; i++) {
  241. hex.Append(NextChar);
  242. }
  243. s.Append((char) Convert.ToInt32(hex.ToString(), 16));
  244. break;
  245. }
  246. break;
  247. default:
  248. s.Append(c);
  249. break;
  250. }
  251. }
  252. return s.ToString();
  253. }
  254. object ParseNumber() {
  255. string number = NextWord;
  256. if (number.IndexOf('.') == -1) {
  257. long parsedInt;
  258. Int64.TryParse(number, out parsedInt);
  259. return parsedInt;
  260. }
  261. double parsedDouble;
  262. Double.TryParse(number, out parsedDouble);
  263. return parsedDouble;
  264. }
  265. void EatWhitespace() {
  266. while (WHITE_SPACE.IndexOf(PeekChar) != -1) {
  267. json.Read();
  268. if (json.Peek() == -1) {
  269. break;
  270. }
  271. }
  272. }
  273. char PeekChar {
  274. get {
  275. return Convert.ToChar(json.Peek());
  276. }
  277. }
  278. char NextChar {
  279. get {
  280. return Convert.ToChar(json.Read());
  281. }
  282. }
  283. string NextWord {
  284. get {
  285. StringBuilder word = new StringBuilder();
  286. while (WORD_BREAK.IndexOf(PeekChar) == -1) {
  287. word.Append(NextChar);
  288. if (json.Peek() == -1) {
  289. break;
  290. }
  291. }
  292. return word.ToString();
  293. }
  294. }
  295. TOKEN NextToken {
  296. get {
  297. EatWhitespace();
  298. if (json.Peek() == -1) {
  299. return TOKEN.NONE;
  300. }
  301. char c = PeekChar;
  302. switch (c) {
  303. case '{':
  304. return TOKEN.CURLY_OPEN;
  305. case '}':
  306. json.Read();
  307. return TOKEN.CURLY_CLOSE;
  308. case '[':
  309. return TOKEN.SQUARED_OPEN;
  310. case ']':
  311. json.Read();
  312. return TOKEN.SQUARED_CLOSE;
  313. case ',':
  314. json.Read();
  315. return TOKEN.COMMA;
  316. case '"':
  317. return TOKEN.STRING;
  318. case ':':
  319. return TOKEN.COLON;
  320. case '0':
  321. case '1':
  322. case '2':
  323. case '3':
  324. case '4':
  325. case '5':
  326. case '6':
  327. case '7':
  328. case '8':
  329. case '9':
  330. case '-':
  331. return TOKEN.NUMBER;
  332. }
  333. string word = NextWord;
  334. switch (word) {
  335. case "false":
  336. return TOKEN.FALSE;
  337. case "true":
  338. return TOKEN.TRUE;
  339. case "null":
  340. return TOKEN.NULL;
  341. }
  342. return TOKEN.NONE;
  343. }
  344. }
  345. }
  346. /// <summary>
  347. /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string
  348. /// </summary>
  349. /// <param name="json">A Dictionary&lt;string, object&gt; / List&lt;object&gt;</param>
  350. /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
  351. public static string Serialize(object obj) {
  352. return Serializer.Serialize(obj);
  353. }
  354. sealed class Serializer {
  355. StringBuilder builder;
  356. Serializer() {
  357. builder = new StringBuilder();
  358. }
  359. public static string Serialize(object obj) {
  360. var instance = new Serializer();
  361. instance.SerializeValue(obj);
  362. return instance.builder.ToString();
  363. }
  364. void SerializeValue(object value) {
  365. IList asList;
  366. IDictionary asDict;
  367. string asStr;
  368. if (value == null) {
  369. builder.Append("null");
  370. }
  371. else if ((asStr = value as string) != null) {
  372. SerializeString(asStr);
  373. }
  374. else if (value is bool) {
  375. builder.Append(value.ToString().ToLower());
  376. }
  377. else if ((asList = value as IList) != null) {
  378. SerializeArray(asList);
  379. }
  380. else if ((asDict = value as IDictionary) != null) {
  381. SerializeObject(asDict);
  382. }
  383. else if (value is char) {
  384. SerializeString(value.ToString());
  385. }
  386. else {
  387. SerializeOther(value);
  388. }
  389. }
  390. void SerializeObject(IDictionary obj) {
  391. bool first = true;
  392. builder.Append('{');
  393. foreach (object e in obj.Keys) {
  394. if (!first) {
  395. builder.Append(',');
  396. }
  397. SerializeString(e.ToString());
  398. builder.Append(':');
  399. SerializeValue(obj[e]);
  400. first = false;
  401. }
  402. builder.Append('}');
  403. }
  404. void SerializeArray(IList anArray) {
  405. builder.Append('[');
  406. bool first = true;
  407. foreach (object obj in anArray) {
  408. if (!first) {
  409. builder.Append(',');
  410. }
  411. SerializeValue(obj);
  412. first = false;
  413. }
  414. builder.Append(']');
  415. }
  416. void SerializeString(string str) {
  417. builder.Append('\"');
  418. char[] charArray = str.ToCharArray();
  419. foreach (var c in charArray) {
  420. switch (c) {
  421. case '"':
  422. builder.Append("\\\"");
  423. break;
  424. case '\\':
  425. builder.Append("\\\\");
  426. break;
  427. case '\b':
  428. builder.Append("\\b");
  429. break;
  430. case '\f':
  431. builder.Append("\\f");
  432. break;
  433. case '\n':
  434. builder.Append("\\n");
  435. break;
  436. case '\r':
  437. builder.Append("\\r");
  438. break;
  439. case '\t':
  440. builder.Append("\\t");
  441. break;
  442. default:
  443. int codepoint = Convert.ToInt32(c);
  444. if ((codepoint >= 32) && (codepoint <= 126)) {
  445. builder.Append(c);
  446. }
  447. else {
  448. builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
  449. }
  450. break;
  451. }
  452. }
  453. builder.Append('\"');
  454. }
  455. void SerializeOther(object value) {
  456. if (value is float
  457. || value is int
  458. || value is uint
  459. || value is long
  460. || value is double
  461. || value is sbyte
  462. || value is byte
  463. || value is short
  464. || value is ushort
  465. || value is ulong
  466. || value is decimal) {
  467. builder.Append(value.ToString());
  468. }
  469. else {
  470. SerializeString(value.ToString());
  471. }
  472. }
  473. }
  474. }
  475. #region Extension methods
  476. public static class MiniJsonExtensions
  477. {
  478. public static string toJson( this Dictionary<string,object> obj )
  479. {
  480. return Json.Serialize( obj );
  481. }
  482. public static List<object> listFromJson( this string json )
  483. {
  484. return (List<object>) Json.Deserialize( json );
  485. }
  486. public static Dictionary<string,object> dictionaryFromJson( this string json )
  487. {
  488. return (Dictionary<string,object>) Json.Deserialize( json );
  489. }
  490. }
  491. #endregion