/// <summary> /// Parse a JSON string to generate objects. /// </summary> /// <param name="json">The JSON string</param> /// <returns>A .NET object</returns> public static JNode Parse(string json) { int rp = 0; int rmax = json.Length; int[] istack = new int[32]; int isp = 0; object[] ostack = new object[32]; int osp = 0; int state = ROOT; string key = null; JArray list = null; JObject hash = null; JNode item = null; StringBuilder sb; char ctmp; int itmp, jtmp; while (true) { while (rp < rmax && char.IsWhiteSpace(json [rp])) { rp++; } if (rp == rmax) { throw new JSONParseException("unexpected EOF at " + rp); } switch (json [rp]) { case '[': if (isp + 2 > istack.Length) { Array.Resize(ref istack, isp * 2); } if (osp + 2 > ostack.Length) { Array.Resize(ref ostack, osp * 2); } istack [isp++] = state; ostack [osp++] = list; list = new JArray(); state = LIST_START; rp++; break; case '{': if (isp + 2 > istack.Length) { Array.Resize(ref istack, isp * 2); } if (osp + 2 > ostack.Length) { Array.Resize(ref ostack, osp * 2); } istack [isp++] = state; ostack [osp++] = key; ostack [osp++] = hash; hash = new JObject(); state = HASH_START; rp++; break; case ']': if (state == LIST_START || state == LIST_ITEM) { rp++; item = list; list = (JArray)ostack [--osp]; state = istack [--isp]; goto add_item; } else { throw new JSONParseException("unexpected CLOSE BRACKET at " + rp); } case '}': if (state == HASH_START || state == HASH_ITEM) { rp++; item = hash; hash = (JObject)ostack [--osp]; key = (string)ostack [--osp]; state = istack [--isp]; goto add_item; } else { throw new JSONParseException("unexpected CLOSE BRACE at " + rp); } case ':': if (state == HASH_KEY) { state = HASH_COLON; } else { throw new JSONParseException("unexpected COLON at " + rp); } rp++; break; case ',': if (state == HASH_ITEM) { state = HASH_COMMA; } else if (state == LIST_ITEM) { state = LIST_COMMA; } else { throw new JSONParseException("unexpected COMMA at " + rp); } rp++; break; case 't': if (rp + 4 <= rmax && json.Substring(rp, 4) == "true") { item = true; rp += 4; goto add_item; } goto default; case 'f': if (rp + 5 <= rmax && json.Substring(rp, 5) == "false") { item = false; rp += 5; goto add_item; } goto default; case 'n': if (rp + 4 <= rmax && json.Substring(rp, 4) == "null") { item = null; rp += 4; goto add_item; } goto default; case '"': rp++; sb = new StringBuilder(); while (true) { if (rp == rmax) { throw new JSONParseException("unexpected EOF in string at " + rp); } ctmp = json [rp]; if (char.IsControl(ctmp)) { throw new JSONParseException("control character in string at " + rp); } rp++; if (ctmp == '"') { break; } if (ctmp != '\\') { sb.Append(ctmp); continue; } if (rp == rmax) { throw new JSONParseException("unexpected EOF in string at " + rp); } switch (json [rp++]) { case '"': sb.Append('"'); break; case '\\': sb.Append('\\'); break; case '/': sb.Append('/'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 't': sb.Append('\t'); break; case 'u': if (rp + 4 > rmax) { throw new JSONParseException("unexpected EOF in hex escape at " + rp); } jtmp = 0; for (itmp = 0; itmp < 4; itmp++) { ctmp = json [rp++]; jtmp = (jtmp << 4) + (int)ctmp; if (ctmp >= '0' && ctmp <= '9') { jtmp -= (int)'0'; } else if (ctmp >= 'a' && ctmp <= 'f') { jtmp -= ((int)'a' - 10); } else if (ctmp >= 'A' && ctmp <= 'F') { jtmp -= ((int)'A' - 10); } else { throw new JSONParseException("invalid hex digit at " + rp); } } sb.Append((char)jtmp); break; default: throw new JSONParseException("unknown backslash escape at " + rp); } } item = sb.ToString(); goto add_item; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': itmp = rp; if (rp < rmax && json [rp] == '-') { rp++; } if (rp < rmax && json [rp] == '0') { rp++; } else if (rp < rmax && json [rp] >= '1' && json [rp] <= '9') { rp++; while (rp < rmax && json [rp] >= '0' && json [rp] <= '9') { rp++; } } else { throw new JSONParseException("malformed number at " + rp); } if (rp < rmax && json [rp] == '.') { rp++; if (rp < rmax && json [rp] >= '0' && json [rp] <= '9') { rp++; } else { throw new JSONParseException("malformed number at " + rp); } while (rp < rmax && json [rp] >= '0' && json [rp] <= '9') { rp++; } } if (rp < rmax && (json [rp] == 'e' || json [rp] == 'E')) { rp++; if (rp < rmax && (json [rp] == '+' || json [rp] == '-')) { rp++; } if (rp < rmax && json [rp] >= '0' && json [rp] <= '9') { rp++; } else { throw new JSONParseException("malformed number at " + rp); } while (rp < rmax && json [rp] >= '0' && json [rp] <= '9') { rp++; } } item = double.Parse(json.Substring(itmp, rp - itmp), NumberFormatInfo.InvariantInfo); goto add_item; default: throw new JSONParseException("unrecognized token at " + rp); } continue; add_item: if (state == HASH_START || state == HASH_COMMA) { JString js = item as JString; if (js == null) { throw new JSONParseException("non-string hash key at " + rp); } js.TryString(out key); state = HASH_KEY; } else if (state == HASH_COLON) { hash [key] = item; state = HASH_ITEM; } else if (state == LIST_START || state == LIST_COMMA) { list.Add(item); state = LIST_ITEM; } else if (state == ROOT) { while (rp < rmax && char.IsWhiteSpace(json [rp])) { rp++; } if (rp < rmax) { throw new JSONParseException("trailing garbage at " + rp); } return(item); } else { throw new JSONParseException("unexpected ITEM at " + rp); } continue; } }