You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JSON.cs 12KB

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