JsonParser.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Text;
  6. namespace FastJSON
  7. {
  8. /// <summary>
  9. /// This class encodes and decodes JSON strings.
  10. /// Spec. details, see http://www.json.org/
  11. /// </summary>
  12. internal sealed class JsonParser
  13. {
  14. enum Token
  15. {
  16. None = -1, // Used to denote no Lookahead available
  17. Curly_Open,
  18. Curly_Close,
  19. Squared_Open,
  20. Squared_Close,
  21. Colon,
  22. Comma,
  23. String,
  24. Number,
  25. True,
  26. False,
  27. Null
  28. }
  29. readonly string json;
  30. readonly StringBuilder s = new StringBuilder();
  31. Token lookAheadToken = Token.None;
  32. int index;
  33. internal JsonParser(string json)
  34. {
  35. this.json = json;
  36. }
  37. public object Decode()
  38. {
  39. return ParseValue();
  40. }
  41. private Dictionary<string, object> ParseObject()
  42. {
  43. Dictionary<string, object> table = new Dictionary<string, object>();
  44. ConsumeToken(); // {
  45. while (true)
  46. {
  47. switch (LookAhead())
  48. {
  49. case Token.Comma:
  50. ConsumeToken();
  51. break;
  52. case Token.Curly_Close:
  53. ConsumeToken();
  54. return table;
  55. default:
  56. {
  57. // name
  58. string name = ParseString();
  59. // :
  60. if (NextToken() != Token.Colon)
  61. {
  62. throw new Exception("Expected colon at index " + index);
  63. }
  64. // value
  65. object value = ParseValue();
  66. table[name] = value;
  67. }
  68. break;
  69. }
  70. }
  71. }
  72. private List<object> ParseArray()
  73. {
  74. List<object> array = new List<object>();
  75. ConsumeToken(); // [
  76. while (true)
  77. {
  78. switch (LookAhead())
  79. {
  80. case Token.Comma:
  81. ConsumeToken();
  82. break;
  83. case Token.Squared_Close:
  84. ConsumeToken();
  85. return array;
  86. default:
  87. array.Add(ParseValue());
  88. break;
  89. }
  90. }
  91. }
  92. private object ParseValue()
  93. {
  94. switch (LookAhead())
  95. {
  96. case Token.Number:
  97. return ParseNumber();
  98. case Token.String:
  99. return ParseString();
  100. case Token.Curly_Open:
  101. return ParseObject();
  102. case Token.Squared_Open:
  103. return ParseArray();
  104. case Token.True:
  105. ConsumeToken();
  106. return true;
  107. case Token.False:
  108. ConsumeToken();
  109. return false;
  110. case Token.Null:
  111. ConsumeToken();
  112. return null;
  113. }
  114. throw new Exception("Unrecognized token at index" + index);
  115. }
  116. private string ParseString()
  117. {
  118. ConsumeToken(); // "
  119. s.Length = 0;
  120. int runIndex = -1;
  121. while (index < json.Length)
  122. {
  123. var c = json[index++];
  124. if (c == '"')
  125. {
  126. if (runIndex != -1)
  127. {
  128. if (s.Length == 0)
  129. return json.Substring(runIndex, index - runIndex - 1);
  130. s.Append(json, runIndex, index - runIndex - 1);
  131. }
  132. return s.ToString();
  133. }
  134. if (c != '\\')
  135. {
  136. if (runIndex == -1)
  137. runIndex = index - 1;
  138. continue;
  139. }
  140. if (index == json.Length) break;
  141. if (runIndex != -1)
  142. {
  143. s.Append(json, runIndex, index - runIndex - 1);
  144. runIndex = -1;
  145. }
  146. switch (json[index++])
  147. {
  148. case '"':
  149. s.Append('"');
  150. break;
  151. case '\\':
  152. s.Append('\\');
  153. break;
  154. case '/':
  155. s.Append('/');
  156. break;
  157. case 'b':
  158. s.Append('\b');
  159. break;
  160. case 'f':
  161. s.Append('\f');
  162. break;
  163. case 'n':
  164. s.Append('\n');
  165. break;
  166. case 'r':
  167. s.Append('\r');
  168. break;
  169. case 't':
  170. s.Append('\t');
  171. break;
  172. case 'u':
  173. {
  174. int remainingLength = json.Length - index;
  175. if (remainingLength < 4) break;
  176. // parse the 32 bit hex into an integer codepoint
  177. uint codePoint = ParseUnicode(json[index], json[index + 1], json[index + 2], json[index + 3]);
  178. s.Append((char)codePoint);
  179. // skip 4 chars
  180. index += 4;
  181. }
  182. break;
  183. }
  184. }
  185. throw new Exception("Unexpectedly reached end of string");
  186. }
  187. private uint ParseSingleChar(char c1, uint multipliyer)
  188. {
  189. uint p1 = 0;
  190. if (c1 >= '0' && c1 <= '9')
  191. p1 = (uint)(c1 - '0') * multipliyer;
  192. else if (c1 >= 'A' && c1 <= 'F')
  193. p1 = (uint)((c1 - 'A') + 10) * multipliyer;
  194. else if (c1 >= 'a' && c1 <= 'f')
  195. p1 = (uint)((c1 - 'a') + 10) * multipliyer;
  196. return p1;
  197. }
  198. private uint ParseUnicode(char c1, char c2, char c3, char c4)
  199. {
  200. uint p1 = ParseSingleChar(c1, 0x1000);
  201. uint p2 = ParseSingleChar(c2, 0x100);
  202. uint p3 = ParseSingleChar(c3, 0x10);
  203. uint p4 = ParseSingleChar(c4, 1);
  204. return p1 + p2 + p3 + p4;
  205. }
  206. private long CreateLong(string s)
  207. {
  208. long num = 0;
  209. bool neg = false;
  210. foreach (char cc in s)
  211. {
  212. if (cc == '-')
  213. neg = true;
  214. else if (cc == '+')
  215. neg = false;
  216. else
  217. {
  218. num *= 10;
  219. num += (int)(cc - '0');
  220. }
  221. }
  222. return neg ? -num : num;
  223. }
  224. private object ParseNumber()
  225. {
  226. ConsumeToken();
  227. // Need to start back one place because the first digit is also a token and would have been consumed
  228. var startIndex = index - 1;
  229. bool dec = false;
  230. do
  231. {
  232. if (index == json.Length)
  233. break;
  234. var c = json[index];
  235. if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E')
  236. {
  237. if (c == '.' || c == 'e' || c == 'E')
  238. dec = true;
  239. if (++index == json.Length)
  240. break;//throw new Exception("Unexpected end of string whilst parsing number");
  241. continue;
  242. }
  243. break;
  244. } while (true);
  245. if (dec)
  246. {
  247. string s = json.Substring(startIndex, index - startIndex);
  248. return double.Parse(s, NumberFormatInfo.InvariantInfo);
  249. }
  250. long num;
  251. return JSON.CreateLong(out num, json, startIndex, index - startIndex);
  252. }
  253. private Token LookAhead()
  254. {
  255. if (lookAheadToken != Token.None) return lookAheadToken;
  256. return lookAheadToken = NextTokenCore();
  257. }
  258. private void ConsumeToken()
  259. {
  260. lookAheadToken = Token.None;
  261. }
  262. private Token NextToken()
  263. {
  264. var result = lookAheadToken != Token.None ? lookAheadToken : NextTokenCore();
  265. lookAheadToken = Token.None;
  266. return result;
  267. }
  268. private Token NextTokenCore()
  269. {
  270. char c;
  271. // Skip past whitespace
  272. do
  273. {
  274. c = json[index];
  275. if (c > ' ') break;
  276. if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
  277. } while (++index < json.Length);
  278. if (index == json.Length)
  279. {
  280. throw new Exception("Reached end of string unexpectedly");
  281. }
  282. c = json[index];
  283. index++;
  284. switch (c)
  285. {
  286. case '{':
  287. return Token.Curly_Open;
  288. case '}':
  289. return Token.Curly_Close;
  290. case '[':
  291. return Token.Squared_Open;
  292. case ']':
  293. return Token.Squared_Close;
  294. case ',':
  295. return Token.Comma;
  296. case '"':
  297. return Token.String;
  298. case '0':
  299. case '1':
  300. case '2':
  301. case '3':
  302. case '4':
  303. case '5':
  304. case '6':
  305. case '7':
  306. case '8':
  307. case '9':
  308. case '-':
  309. case '+':
  310. case '.':
  311. return Token.Number;
  312. case ':':
  313. return Token.Colon;
  314. case 'f':
  315. if (json.Length - index >= 4 &&
  316. json[index + 0] == 'a' &&
  317. json[index + 1] == 'l' &&
  318. json[index + 2] == 's' &&
  319. json[index + 3] == 'e')
  320. {
  321. index += 4;
  322. return Token.False;
  323. }
  324. break;
  325. case 't':
  326. if (json.Length - index >= 3 &&
  327. json[index + 0] == 'r' &&
  328. json[index + 1] == 'u' &&
  329. json[index + 2] == 'e')
  330. {
  331. index += 3;
  332. return Token.True;
  333. }
  334. break;
  335. case 'n':
  336. if (json.Length - index >= 3 &&
  337. json[index + 0] == 'u' &&
  338. json[index + 1] == 'l' &&
  339. json[index + 2] == 'l')
  340. {
  341. index += 3;
  342. return Token.Null;
  343. }
  344. break;
  345. }
  346. throw new Exception("Could not find token at index " + --index);
  347. }
  348. }
  349. }