using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; namespace MiniJSONV { public static class Json { public static object Deserialize(string json) { if (json == null) { return null; } return Json.Parser.Parse(json); } public static string Serialize(object obj) { return Json.Serializer.Serialize(obj); } private sealed class Parser : IDisposable { private Parser(string jsonString) { this.json = new StringReader(jsonString); } public static bool IsWordBreak(char c) { return char.IsWhiteSpace(c) || "{}[],:\"".IndexOf(c) != -1; } public static object Parse(string jsonString) { object result; using (Json.Parser parser = new Json.Parser(jsonString)) { result = parser.ParseValue(); } return result; } public void Dispose() { this.json.Dispose(); this.json = null; } private Dictionary ParseObject() { Dictionary dictionary = new Dictionary(); this.json.Read(); for (;;) { Json.Parser.TOKEN nextToken = this.NextToken; switch (nextToken) { case Json.Parser.TOKEN.NONE: goto IL_37; default: if (nextToken != Json.Parser.TOKEN.COMMA) { string text = this.ParseString(); if (text == null) { goto Block_2; } if (this.NextToken != Json.Parser.TOKEN.COLON) { goto Block_3; } this.json.Read(); dictionary[text] = this.ParseValue(); } break; case Json.Parser.TOKEN.CURLY_CLOSE: return dictionary; } } IL_37: return null; Block_2: return null; Block_3: return null; } private List ParseArray() { List list = new List(); this.json.Read(); bool flag = true; while (flag) { Json.Parser.TOKEN nextToken = this.NextToken; switch (nextToken) { case Json.Parser.TOKEN.SQUARED_CLOSE: flag = false; break; default: { if (nextToken == Json.Parser.TOKEN.NONE) { return null; } object item = this.ParseByToken(nextToken); list.Add(item); break; } case Json.Parser.TOKEN.COMMA: break; } } return list; } private object ParseValue() { Json.Parser.TOKEN nextToken = this.NextToken; return this.ParseByToken(nextToken); } private object ParseByToken(Json.Parser.TOKEN token) { switch (token) { case Json.Parser.TOKEN.STRING: return this.ParseString(); case Json.Parser.TOKEN.NUMBER: return this.ParseNumber(); case Json.Parser.TOKEN.TRUE: return true; case Json.Parser.TOKEN.FALSE: return false; case Json.Parser.TOKEN.NULL: return null; default: switch (token) { case Json.Parser.TOKEN.CURLY_OPEN: return this.ParseObject(); case Json.Parser.TOKEN.SQUARED_OPEN: return this.ParseArray(); } return null; } } private string ParseString() { StringBuilder stringBuilder = new StringBuilder(); this.json.Read(); bool flag = true; while (flag) { if (this.json.Peek() == -1) { break; } char nextChar = this.NextChar; if (nextChar != '"') { if (nextChar != '\\') { stringBuilder.Append(nextChar); } else if (this.json.Peek() == -1) { flag = false; } else { nextChar = this.NextChar; switch (nextChar) { case 'r': stringBuilder.Append('\r'); break; default: if (nextChar != '"' && nextChar != '/' && nextChar != '\\') { if (nextChar != 'b') { if (nextChar != 'f') { if (nextChar == 'n') { stringBuilder.Append('\n'); } } else { stringBuilder.Append('\f'); } } else { stringBuilder.Append('\b'); } } else { stringBuilder.Append(nextChar); } break; case 't': stringBuilder.Append('\t'); break; case 'u': { char[] array = new char[4]; for (int i = 0; i < 4; i++) { array[i] = this.NextChar; } stringBuilder.Append((char)Convert.ToInt32(new string(array), 16)); break; } } } } else { flag = false; } } return stringBuilder.ToString(); } private object ParseNumber() { string nextWord = this.NextWord; if (nextWord.IndexOf('.') == -1) { long num; long.TryParse(nextWord, out num); return num; } double num2; double.TryParse(nextWord, out num2); return num2; } private void EatWhitespace() { while (char.IsWhiteSpace(this.PeekChar)) { this.json.Read(); if (this.json.Peek() == -1) { break; } } } private char PeekChar { get { return Convert.ToChar(this.json.Peek()); } } private char NextChar { get { return Convert.ToChar(this.json.Read()); } } private string NextWord { get { StringBuilder stringBuilder = new StringBuilder(); while (!Json.Parser.IsWordBreak(this.PeekChar)) { stringBuilder.Append(this.NextChar); if (this.json.Peek() == -1) { break; } } return stringBuilder.ToString(); } } private Json.Parser.TOKEN NextToken { get { this.EatWhitespace(); if (this.json.Peek() == -1) { return Json.Parser.TOKEN.NONE; } char peekChar = this.PeekChar; switch (peekChar) { case ',': this.json.Read(); return Json.Parser.TOKEN.COMMA; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return Json.Parser.TOKEN.NUMBER; default: switch (peekChar) { case '[': return Json.Parser.TOKEN.SQUARED_OPEN; default: switch (peekChar) { case '{': return Json.Parser.TOKEN.CURLY_OPEN; default: if (peekChar != '"') { string nextWord = this.NextWord; if (nextWord != null) { if (nextWord == "false") { return Json.Parser.TOKEN.FALSE; } if (nextWord == "true") { return Json.Parser.TOKEN.TRUE; } if (nextWord == "null") { return Json.Parser.TOKEN.NULL; } } return Json.Parser.TOKEN.NONE; } return Json.Parser.TOKEN.STRING; case '}': this.json.Read(); return Json.Parser.TOKEN.CURLY_CLOSE; } break; case ']': this.json.Read(); return Json.Parser.TOKEN.SQUARED_CLOSE; } break; case ':': return Json.Parser.TOKEN.COLON; } } } private const string WORD_BREAK = "{}[],:\""; private StringReader json; private enum TOKEN { NONE, CURLY_OPEN, CURLY_CLOSE, SQUARED_OPEN, SQUARED_CLOSE, COLON, COMMA, STRING, NUMBER, TRUE, FALSE, NULL } } private sealed class Serializer { private Serializer() { this.builder = new StringBuilder(); } public static string Serialize(object obj) { Json.Serializer serializer = new Json.Serializer(); serializer.SerializeValue(obj); return serializer.builder.ToString(); } private void SerializeValue(object value) { string str; IList anArray; IDictionary obj; if (value == null) { this.builder.Append("null"); } else if ((str = (value as string)) != null) { this.SerializeString(str); } else if (value is bool) { this.builder.Append((!(bool)value) ? "false" : "true"); } else if ((anArray = (value as IList)) != null) { this.SerializeArray(anArray); } else if ((obj = (value as IDictionary)) != null) { this.SerializeObject(obj); } else if (value is char) { this.SerializeString(new string((char)value, 1)); } else { this.SerializeOther(value); } } private void SerializeObject(IDictionary obj) { bool flag = true; this.builder.Append('{'); IEnumerator enumerator = obj.Keys.GetEnumerator(); try { while (enumerator.MoveNext()) { object obj2 = enumerator.Current; if (!flag) { this.builder.Append(','); } this.SerializeString(obj2.ToString()); this.builder.Append(':'); this.SerializeValue(obj[obj2]); flag = false; } } finally { IDisposable disposable; if ((disposable = (enumerator as IDisposable)) != null) { disposable.Dispose(); } } this.builder.Append('}'); } private void SerializeArray(IList anArray) { this.builder.Append('['); bool flag = true; IEnumerator enumerator = anArray.GetEnumerator(); try { while (enumerator.MoveNext()) { object value = enumerator.Current; if (!flag) { this.builder.Append(','); } this.SerializeValue(value); flag = false; } } finally { IDisposable disposable; if ((disposable = (enumerator as IDisposable)) != null) { disposable.Dispose(); } } this.builder.Append(']'); } private void SerializeString(string str) { this.builder.Append('"'); char[] array = str.ToCharArray(); foreach (char c in array) { switch (c) { case '\b': this.builder.Append("\\b"); break; case '\t': this.builder.Append("\\t"); break; case '\n': this.builder.Append("\\n"); break; default: if (c != '"') { if (c != '\\') { int num = Convert.ToInt32(c); if (num >= 32 && num <= 126) { this.builder.Append(c); } else { this.builder.Append("\\u"); this.builder.Append(num.ToString("x4")); } } else { this.builder.Append("\\\\"); } } else { this.builder.Append("\\\""); } break; case '\f': this.builder.Append("\\f"); break; case '\r': this.builder.Append("\\r"); break; } } this.builder.Append('"'); } private void SerializeOther(object value) { if (value is float) { this.builder.Append(((float)value).ToString("R")); } else if (value is int || value is uint || value is long || value is sbyte || value is byte || value is short || value is ushort || value is ulong) { this.builder.Append(value); } else if (value is double || value is decimal) { this.builder.Append(Convert.ToDouble(value).ToString("R")); } else { this.SerializeString(value.ToString()); } } private StringBuilder builder; } } }