JSON.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Text;
  6. namespace BestHTTP.JSON
  7. {
  8. /// <summary>
  9. /// Based on the download from http://techblog.procurios.nl/k/news/view/14605/14863/how-do-i-write-my-own-parser-%28for-json%29.html
  10. /// This class encodes and decodes JSON strings.
  11. /// Spec. details, see http://www.json.org/
  12. ///
  13. /// JSON uses Arrays and Objects. These correspond here to the datatypes List and Dictionary.
  14. /// All numbers are parsed to doubles.
  15. /// </summary>
  16. public class Json
  17. {
  18. private const int TOKEN_NONE = 0;
  19. private const int TOKEN_CURLY_OPEN = 1;
  20. private const int TOKEN_CURLY_CLOSE = 2;
  21. private const int TOKEN_SQUARED_OPEN = 3;
  22. private const int TOKEN_SQUARED_CLOSE = 4;
  23. private const int TOKEN_COLON = 5;
  24. private const int TOKEN_COMMA = 6;
  25. private const int TOKEN_STRING = 7;
  26. private const int TOKEN_NUMBER = 8;
  27. private const int TOKEN_TRUE = 9;
  28. private const int TOKEN_FALSE = 10;
  29. private const int TOKEN_NULL = 11;
  30. private const int BUILDER_CAPACITY = 2000;
  31. /// <summary>
  32. /// Parses the string json into a value
  33. /// </summary>
  34. /// <param name="json">A JSON string.</param>
  35. /// <returns>A List, a Dictionary, a double, a string, null, true, or false</returns>
  36. public static object Decode(string json)
  37. {
  38. bool success = true;
  39. return Decode(json, ref success);
  40. }
  41. /// <summary>
  42. /// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
  43. /// </summary>
  44. /// <param name="json">A JSON string.</param>
  45. /// <param name="success">Successful parse?</param>
  46. /// <returns>A List, a Dictionary, a double, a string, null, true, or false</returns>
  47. public static object Decode(string json, ref bool success)
  48. {
  49. success = true;
  50. if (json != null) {
  51. char[] charArray = json.ToCharArray();
  52. int index = 0;
  53. object value = ParseValue(charArray, ref index, ref success);
  54. return value;
  55. } else {
  56. return null;
  57. }
  58. }
  59. /// <summary>
  60. /// Converts a Dictionary / List object into a JSON string
  61. /// </summary>
  62. /// <param name="json">A Dictionary / List</param>
  63. /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
  64. public static string Encode(object json)
  65. {
  66. StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
  67. bool success = SerializeValue(json, builder);
  68. return (success ? builder.ToString() : null);
  69. }
  70. protected static Dictionary<string, object> ParseObject(char[] json, ref int index, ref bool success)
  71. {
  72. Dictionary<string, object> table = new Dictionary<string, object>();
  73. int token;
  74. // {
  75. NextToken(json, ref index);
  76. bool done = false;
  77. while (!done) {
  78. token = LookAhead(json, index);
  79. if (token == Json.TOKEN_NONE) {
  80. success = false;
  81. return null;
  82. } else if (token == Json.TOKEN_COMMA) {
  83. NextToken(json, ref index);
  84. } else if (token == Json.TOKEN_CURLY_CLOSE) {
  85. NextToken(json, ref index);
  86. return table;
  87. } else {
  88. // name
  89. string name = ParseString(json, ref index, ref success);
  90. if (!success) {
  91. success = false;
  92. return null;
  93. }
  94. // :
  95. token = NextToken(json, ref index);
  96. if (token != Json.TOKEN_COLON) {
  97. success = false;
  98. return null;
  99. }
  100. // value
  101. object value = ParseValue(json, ref index, ref success);
  102. if (!success) {
  103. success = false;
  104. return null;
  105. }
  106. table[name] = value;
  107. }
  108. }
  109. return table;
  110. }
  111. protected static List<object> ParseArray(char[] json, ref int index, ref bool success)
  112. {
  113. List<object> array = new List<object>();
  114. // [
  115. NextToken(json, ref index);
  116. bool done = false;
  117. while (!done) {
  118. int token = LookAhead(json, index);
  119. if (token == Json.TOKEN_NONE) {
  120. success = false;
  121. return null;
  122. } else if (token == Json.TOKEN_COMMA) {
  123. NextToken(json, ref index);
  124. } else if (token == Json.TOKEN_SQUARED_CLOSE) {
  125. NextToken(json, ref index);
  126. break;
  127. } else {
  128. object value = ParseValue(json, ref index, ref success);
  129. if (!success) {
  130. return null;
  131. }
  132. array.Add(value);
  133. }
  134. }
  135. return array;
  136. }
  137. protected static object ParseValue(char[] json, ref int index, ref bool success)
  138. {
  139. switch (LookAhead(json, index)) {
  140. case Json.TOKEN_STRING:
  141. return ParseString(json, ref index, ref success);
  142. case Json.TOKEN_NUMBER:
  143. return ParseNumber(json, ref index, ref success);
  144. case Json.TOKEN_CURLY_OPEN:
  145. return ParseObject(json, ref index, ref success);
  146. case Json.TOKEN_SQUARED_OPEN:
  147. return ParseArray(json, ref index, ref success);
  148. case Json.TOKEN_TRUE:
  149. NextToken(json, ref index);
  150. return true;
  151. case Json.TOKEN_FALSE:
  152. NextToken(json, ref index);
  153. return false;
  154. case Json.TOKEN_NULL:
  155. NextToken(json, ref index);
  156. return null;
  157. case Json.TOKEN_NONE:
  158. break;
  159. }
  160. success = false;
  161. return null;
  162. }
  163. protected static string ParseString(char[] json, ref int index, ref bool success)
  164. {
  165. StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
  166. char c;
  167. EatWhitespace(json, ref index);
  168. // "
  169. c = json[index++];
  170. bool complete = false;
  171. while (!complete) {
  172. if (index == json.Length) {
  173. break;
  174. }
  175. c = json[index++];
  176. if (c == '"') {
  177. complete = true;
  178. break;
  179. } else if (c == '\\') {
  180. if (index == json.Length) {
  181. break;
  182. }
  183. c = json[index++];
  184. if (c == '"') {
  185. s.Append('"');
  186. } else if (c == '\\') {
  187. s.Append('\\');
  188. } else if (c == '/') {
  189. s.Append('/');
  190. } else if (c == 'b') {
  191. s.Append('\b');
  192. } else if (c == 'f') {
  193. s.Append('\f');
  194. } else if (c == 'n') {
  195. s.Append('\n');
  196. } else if (c == 'r') {
  197. s.Append('\r');
  198. } else if (c == 't') {
  199. s.Append('\t');
  200. } else if (c == 'u') {
  201. int remainingLength = json.Length - index;
  202. if (remainingLength >= 4) {
  203. // parse the 32 bit hex into an integer codepoint
  204. uint codePoint;
  205. if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) {
  206. return "";
  207. }
  208. // convert the integer codepoint to a unicode char and add to string
  209. s.Append(Char.ConvertFromUtf32((int)codePoint));
  210. // skip 4 chars
  211. index += 4;
  212. } else {
  213. break;
  214. }
  215. }
  216. } else {
  217. s.Append(c);
  218. }
  219. }
  220. if (!complete) {
  221. success = false;
  222. return null;
  223. }
  224. return s.ToString();
  225. }
  226. protected static double ParseNumber(char[] json, ref int index, ref bool success)
  227. {
  228. EatWhitespace(json, ref index);
  229. int lastIndex = GetLastIndexOfNumber(json, index);
  230. int charLength = (lastIndex - index) + 1;
  231. double number;
  232. success = Double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
  233. index = lastIndex + 1;
  234. return number;
  235. }
  236. protected static int GetLastIndexOfNumber(char[] json, int index)
  237. {
  238. int lastIndex;
  239. for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
  240. if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) {
  241. break;
  242. }
  243. }
  244. return lastIndex - 1;
  245. }
  246. protected static void EatWhitespace(char[] json, ref int index)
  247. {
  248. for (; index < json.Length; index++) {
  249. if (" \t\n\r".IndexOf(json[index]) == -1) {
  250. break;
  251. }
  252. }
  253. }
  254. protected static int LookAhead(char[] json, int index)
  255. {
  256. int saveIndex = index;
  257. return NextToken(json, ref saveIndex);
  258. }
  259. protected static int NextToken(char[] json, ref int index)
  260. {
  261. EatWhitespace(json, ref index);
  262. if (index == json.Length) {
  263. return Json.TOKEN_NONE;
  264. }
  265. char c = json[index];
  266. index++;
  267. switch (c) {
  268. case '{':
  269. return Json.TOKEN_CURLY_OPEN;
  270. case '}':
  271. return Json.TOKEN_CURLY_CLOSE;
  272. case '[':
  273. return Json.TOKEN_SQUARED_OPEN;
  274. case ']':
  275. return Json.TOKEN_SQUARED_CLOSE;
  276. case ',':
  277. return Json.TOKEN_COMMA;
  278. case '"':
  279. return Json.TOKEN_STRING;
  280. case '0': case '1': case '2': case '3': case '4':
  281. case '5': case '6': case '7': case '8': case '9':
  282. case '-':
  283. return Json.TOKEN_NUMBER;
  284. case ':':
  285. return Json.TOKEN_COLON;
  286. }
  287. index--;
  288. int remainingLength = json.Length - index;
  289. // false
  290. if (remainingLength >= 5) {
  291. if (json[index] == 'f' &&
  292. json[index + 1] == 'a' &&
  293. json[index + 2] == 'l' &&
  294. json[index + 3] == 's' &&
  295. json[index + 4] == 'e') {
  296. index += 5;
  297. return Json.TOKEN_FALSE;
  298. }
  299. }
  300. // true
  301. if (remainingLength >= 4) {
  302. if (json[index] == 't' &&
  303. json[index + 1] == 'r' &&
  304. json[index + 2] == 'u' &&
  305. json[index + 3] == 'e') {
  306. index += 4;
  307. return Json.TOKEN_TRUE;
  308. }
  309. }
  310. // null
  311. if (remainingLength >= 4) {
  312. if (json[index] == 'n' &&
  313. json[index + 1] == 'u' &&
  314. json[index + 2] == 'l' &&
  315. json[index + 3] == 'l') {
  316. index += 4;
  317. return Json.TOKEN_NULL;
  318. }
  319. }
  320. return Json.TOKEN_NONE;
  321. }
  322. protected static bool SerializeValue(object value, StringBuilder builder)
  323. {
  324. bool success = true;
  325. if (value is string) {
  326. success = SerializeString((string)value, builder);
  327. } else if (value is IDictionary) {
  328. success = SerializeObject((IDictionary)value, builder);
  329. } else if (value is IList) {
  330. success = SerializeArray(value as IList, builder);
  331. } else if ((value is Boolean) && ((Boolean)value == true)) {
  332. builder.Append("true");
  333. } else if ((value is Boolean) && ((Boolean)value == false)) {
  334. builder.Append("false");
  335. } else if (value is ValueType) {
  336. // thanks to ritchie for pointing out ValueType to me
  337. success = SerializeNumber(Convert.ToDouble(value), builder);
  338. } else if (value == null) {
  339. builder.Append("null");
  340. } else {
  341. success = false;
  342. }
  343. return success;
  344. }
  345. protected static bool SerializeObject(IDictionary anObject, StringBuilder builder)
  346. {
  347. builder.Append("{");
  348. IDictionaryEnumerator e = anObject.GetEnumerator();
  349. bool first = true;
  350. while (e.MoveNext()) {
  351. string key = e.Key.ToString();
  352. object value = e.Value;
  353. if (!first) {
  354. builder.Append(", ");
  355. }
  356. SerializeString(key, builder);
  357. builder.Append(":");
  358. if (!SerializeValue(value, builder)) {
  359. return false;
  360. }
  361. first = false;
  362. }
  363. builder.Append("}");
  364. return true;
  365. }
  366. protected static bool SerializeArray(IList anArray, StringBuilder builder)
  367. {
  368. builder.Append("[");
  369. bool first = true;
  370. for (int i = 0; i < anArray.Count; i++) {
  371. object value = anArray[i];
  372. if (!first) {
  373. builder.Append(", ");
  374. }
  375. if (!SerializeValue(value, builder)) {
  376. return false;
  377. }
  378. first = false;
  379. }
  380. builder.Append("]");
  381. return true;
  382. }
  383. protected static bool SerializeString(string aString, StringBuilder builder)
  384. {
  385. builder.Append("\"");
  386. char[] charArray = aString.ToCharArray();
  387. for (int i = 0; i < charArray.Length; i++) {
  388. char c = charArray[i];
  389. if (c == '"') {
  390. builder.Append("\\\"");
  391. } else if (c == '\\') {
  392. builder.Append("\\\\");
  393. } else if (c == '\b') {
  394. builder.Append("\\b");
  395. } else if (c == '\f') {
  396. builder.Append("\\f");
  397. } else if (c == '\n') {
  398. builder.Append("\\n");
  399. } else if (c == '\r') {
  400. builder.Append("\\r");
  401. } else if (c == '\t') {
  402. builder.Append("\\t");
  403. } else {
  404. int codepoint = Convert.ToInt32(c);
  405. if ((codepoint >= 32) && (codepoint <= 126)) {
  406. builder.Append(c);
  407. } else {
  408. builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
  409. }
  410. }
  411. }
  412. builder.Append("\"");
  413. return true;
  414. }
  415. protected static bool SerializeNumber(double number, StringBuilder builder)
  416. {
  417. builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
  418. return true;
  419. }
  420. }
  421. }