using System; using System.Collections; using System.Collections.Generic; using System.Text; internal class JsonParser { private enum Token { None = -1, Curly_Open, Curly_Close, Squared_Open, Squared_Close, Colon, Comma, String, Number, True, False, Null } private readonly char[] json; private readonly StringBuilder s = new StringBuilder(); private Token lookAheadToken = Token.None; private int index; private bool _ignorecase = false; internal JsonParser(string json, bool ignorecase) { this.json = json.ToCharArray(); _ignorecase = ignorecase; } public object Decode() { return ParseValue(); } private Dictionary ParseObject() { Dictionary table = new Dictionary(); ConsumeToken(); while (true) { switch (LookAhead()) { case Token.Comma: ConsumeToken(); continue; case Token.Curly_Close: ConsumeToken(); return table; } string name = ParseString(); if (_ignorecase) { name = name.ToLower(); } if (NextToken() != Token.Colon) { throw new Exception("Expected colon at index " + index); } object value = (table[name] = ParseValue()); } } private ArrayList ParseArray() { ArrayList array = new ArrayList(); ConsumeToken(); while (true) { switch (LookAhead()) { case Token.Comma: ConsumeToken(); break; case Token.Squared_Close: ConsumeToken(); return array; default: array.Add(ParseValue()); break; } } } private object ParseValue() { switch (LookAhead()) { case Token.Number: return ParseNumber(); case Token.String: return ParseString(); case Token.Curly_Open: return ParseObject(); case Token.Squared_Open: return ParseArray(); case Token.True: ConsumeToken(); return true; case Token.False: ConsumeToken(); return false; case Token.Null: ConsumeToken(); return null; default: throw new Exception("Unrecognized token at index" + index); } } private string ParseString() { ConsumeToken(); s.Length = 0; int runIndex = -1; while (index < json.Length) { switch (json[index++]) { case '"': if (runIndex != -1) { if (s.Length == 0) { return new string(json, runIndex, index - runIndex - 1); } s.Append(json, runIndex, index - runIndex - 1); } return s.ToString(); default: if (runIndex == -1) { runIndex = index - 1; } continue; case '\\': break; } if (index == json.Length) { break; } if (runIndex != -1) { s.Append(json, runIndex, index - runIndex - 1); runIndex = -1; } switch (json[index++]) { case '"': s.Append('"'); break; case '\\': s.Append('\\'); break; case '/': s.Append('/'); break; case 'b': s.Append('\b'); break; case 'f': s.Append('\f'); break; case 'n': s.Append('\n'); break; case 'r': s.Append('\r'); break; case 't': s.Append('\t'); break; case 'u': { int remainingLength = json.Length - index; if (remainingLength >= 4) { uint codePoint = ParseUnicode(json[index], json[index + 1], json[index + 2], json[index + 3]); s.Append((char)codePoint); index += 4; } break; } } } throw new Exception("Unexpectedly reached end of string"); } private uint ParseSingleChar(char c1, uint multipliyer) { uint p1 = 0u; if (c1 >= '0' && c1 <= '9') { p1 = (uint)(c1 - 48) * multipliyer; } else if (c1 >= 'A' && c1 <= 'F') { p1 = (uint)(c1 - 65 + 10) * multipliyer; } else if (c1 >= 'a' && c1 <= 'f') { p1 = (uint)(c1 - 97 + 10) * multipliyer; } return p1; } private uint ParseUnicode(char c1, char c2, char c3, char c4) { uint p1 = ParseSingleChar(c1, 4096u); uint p2 = ParseSingleChar(c2, 256u); uint p3 = ParseSingleChar(c3, 16u); uint p4 = ParseSingleChar(c4, 1u); return p1 + p2 + p3 + p4; } private string ParseNumber() { ConsumeToken(); int startIndex = index - 1; while (true) { char c = json[index]; if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E') { if (++index == json.Length) { throw new Exception("Unexpected end of string whilst parsing number"); } bool flag = true; continue; } break; } return new string(json, startIndex, index - startIndex); } private Token LookAhead() { if (lookAheadToken != Token.None) { return lookAheadToken; } return lookAheadToken = NextTokenCore(); } private void ConsumeToken() { lookAheadToken = Token.None; } private Token NextToken() { Token result = ((lookAheadToken != Token.None) ? lookAheadToken : NextTokenCore()); lookAheadToken = Token.None; return result; } private Token NextTokenCore() { char c; do { c = json[index]; } while (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r') && ++index < json.Length); if (index == json.Length) { throw new Exception("Reached end of string unexpectedly"); } c = json[index]; index++; switch (c) { case '{': return Token.Curly_Open; case '}': return Token.Curly_Close; case '[': return Token.Squared_Open; case ']': return Token.Squared_Close; case ',': return Token.Comma; case '"': return Token.String; case '+': case '-': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return Token.Number; case ':': return Token.Colon; case 'f': if (json.Length - index >= 4 && json[index] == 'a' && json[index + 1] == 'l' && json[index + 2] == 's' && json[index + 3] == 'e') { index += 4; return Token.False; } break; case 't': if (json.Length - index >= 3 && json[index] == 'r' && json[index + 1] == 'u' && json[index + 2] == 'e') { index += 3; return Token.True; } break; case 'n': if (json.Length - index >= 3 && json[index] == 'u' && json[index + 1] == 'l' && json[index + 2] == 'l') { index += 3; return Token.Null; } break; } throw new Exception("Could not find token at index " + --index); } }