+++ /dev/null
-using System;\r
-using System.Collections;\r
-using System.Collections.Generic;\r
-using System.Collections.Specialized;\r
-using System.Dynamic;\r
-using System.Linq;\r
-using System.Reflection;\r
-using System.Text;\r
-using System.Text.RegularExpressions;\r
-\r
-namespace KancolleSniffer.Util\r
-{\r
- public class JsonParser\r
- {\r
- private readonly string _source;\r
- private int _position;\r
-\r
- public static JsonObject Parse(string json)\r
- {\r
- return new JsonParser(json).Parse();\r
- }\r
-\r
- private JsonParser(string json)\r
- {\r
- _source = json;\r
- }\r
-\r
- private JsonObject Parse()\r
- {\r
- var ch = NextChar();\r
- if ('0' <= ch && ch <= '9' || ch == '-')\r
- return ParseNumber();\r
- switch (ch)\r
- {\r
- case 'n':\r
- if (IsMatch("null"))\r
- return null;\r
- goto invalid;\r
- case 't':\r
- if (IsMatch("true"))\r
- return new JsonObject(true);\r
- goto invalid;\r
- case 'f':\r
- if (IsMatch("false"))\r
- return new JsonObject(false);\r
- goto invalid;\r
- case '"':\r
- return ParseString();\r
- case '[':\r
- return ParseArray();\r
- case '{':\r
- return ParseObject();\r
- }\r
- invalid:\r
- throw new JsonParserException($"'{ch}'不正な文字です。 at {_position}");\r
- }\r
-\r
- private bool IsMatch(string s)\r
- {\r
- foreach (char ch in s)\r
- {\r
- var src = LookAhead();\r
- if (ch != src)\r
- return false;\r
- Consume();\r
- }\r
- return true;\r
- }\r
-\r
- private JsonObject ParseNumber()\r
- {\r
- return new JsonObject(GetNumber());\r
- }\r
-\r
- private double GetNumber()\r
- {\r
- var result = 0d;\r
- var ch = LookAhead();\r
- var sign = 1d;\r
- if (ch == '-')\r
- {\r
- sign = -1;\r
- Consume();\r
- ch = LookAhead();\r
- }\r
- while ('0' <= ch && ch <= '9')\r
- {\r
- result = (result * 10.0) + (ch - '0');\r
- Consume();\r
- ch = LookAhead();\r
- }\r
- if (ch != '.')\r
- return sign * result;\r
- var exp = 0.1;\r
- Consume();\r
- ch = LookAhead();\r
- while ('0' <= ch && ch <= '9')\r
- {\r
- result += (ch - '0') * exp;\r
- exp *= 0.1;\r
- Consume();\r
- ch = LookAhead();\r
- }\r
- return sign * result;\r
- }\r
-\r
- private JsonObject ParseString()\r
- {\r
- return new JsonObject(GetString());\r
- }\r
-\r
- private string GetString()\r
- {\r
- Consume();\r
- var len = 0;\r
- while (true)\r
- {\r
- var ch = LookAhead();\r
- if (ch == '\\')\r
- {\r
- Consume();\r
- Consume();\r
- len += 2;\r
- continue;\r
- }\r
- if (ch == '"')\r
- {\r
- Consume();\r
- break;\r
- }\r
- len++;\r
- Consume();\r
- }\r
- return UnEscape(_source.Substring(_position - len - 1, len));\r
- }\r
-\r
- private static readonly Regex EscapeRegex =\r
- new Regex(@"\\[^u]|\\u(?:[0-9A-Fa-f]{4})", RegexOptions.Compiled);\r
-\r
- private string UnEscape(string s)\r
- {\r
- return EscapeRegex.Replace(s, m =>\r
- {\r
- switch (m.Value[1])\r
- {\r
- case '/':\r
- return '/'.ToString();\r
- case '"':\r
- case '\\':\r
- case 'b':\r
- case 'f':\r
- case 'n':\r
- case 'r':\r
- case 't':\r
- case 'u':\r
- return Regex.Unescape(m.Value).ToString();\r
- default:\r
- throw new JsonParserException("不正なエスケープシーケンスです。 at {");\r
- }\r
- });\r
- }\r
-\r
- private JsonObject ParseArray()\r
- {\r
- Consume();\r
- var ary = new List<JsonObject>();\r
- while (true)\r
- {\r
- var ch = NextChar();\r
- if (ch == ']')\r
- {\r
- Consume();\r
- return new JsonObject(ary);\r
- }\r
- ary.Add(Parse());\r
- ch = NextChar();\r
- if (ch != ',' && ch != ']')\r
- throw new JsonParserException($"','か']'が必要です。 at {_position}");\r
- if (ch == ']')\r
- {\r
- Consume();\r
- return new JsonObject(ary);\r
- }\r
- Consume();\r
- }\r
- }\r
-\r
- private JsonObject ParseObject()\r
- {\r
- Consume();\r
- var dict = new OrderedDictionary();\r
- while (true)\r
- {\r
- var ch = NextChar();\r
- if (ch == '}')\r
- {\r
- Consume();\r
- return new JsonObject(dict);\r
- }\r
- if (ch != '"')\r
- throw new JsonParserException($"文字列が必要です。 at {_position}");\r
- var key = GetString();\r
- ch = NextChar();\r
- if (ch != ':')\r
- throw new JsonParserException($"':'が必要です。 at {_position}");\r
- Consume();\r
- var value = Parse();\r
- dict.Add(key, value);\r
- ch = NextChar();\r
- if (ch != ',' && ch != '}')\r
- throw new JsonParserException($"','か'}}'が必要です。 at {_position}");\r
- if (ch == '}')\r
- {\r
- Consume();\r
- return new JsonObject(dict);\r
- }\r
- Consume();\r
- }\r
- }\r
-\r
- private char LookAhead()\r
- {\r
- if (_source.Length == _position)\r
- return '\0';\r
- return _source[_position];\r
- }\r
-\r
- private void Consume()\r
- {\r
- if (_source.Length == _position)\r
- throw new JsonParserException($"入力が途切れています。 at {_position}");\r
- _position++;\r
- }\r
-\r
- private char NextChar()\r
- {\r
- while (true)\r
- {\r
- var ch = LookAhead();\r
- if (!(ch == '\r' || ch == '\n' || ch == '\t' || ch == ' '))\r
- return ch;\r
- Consume();\r
- }\r
- }\r
- }\r
-\r
- public class JsonObject : DynamicObject\r
- {\r
- private readonly JsonType _type;\r
- private readonly bool _bool;\r
- private readonly double _number;\r
- private readonly string _string;\r
- private readonly List<JsonObject> _array;\r
- private readonly OrderedDictionary _dict;\r
-\r
- public bool IsArray => _type == JsonType.Array;\r
- public bool IsObject => _type == JsonType.Object;\r
- // ReSharper disable once UnusedMember.Global\r
- public bool IsDefined(string attr) => IsObject && _dict.Contains(attr);\r
-\r
- public JsonObject(bool b)\r
- {\r
- _type = JsonType.Bool;\r
- _bool = b;\r
- }\r
-\r
- public JsonObject(double n)\r
- {\r
- _type = JsonType.Number;\r
- _number = n;\r
- }\r
-\r
- public JsonObject(string s)\r
- {\r
- _type = JsonType.String;\r
- _string = s;\r
- }\r
-\r
- public JsonObject(List<JsonObject> ary)\r
- {\r
- _type = JsonType.Array;\r
- _array = ary;\r
- }\r
-\r
- public JsonObject(OrderedDictionary dict)\r
- {\r
- _type = JsonType.Object;\r
- _dict = dict;\r
- }\r
-\r
- public override bool TryGetMember(GetMemberBinder binder, out object result)\r
- {\r
- result = null;\r
- if (_type != JsonType.Object)\r
- return false;\r
- if (!_dict.Contains(binder.Name))\r
- return false;\r
- result = ((JsonObject)_dict[binder.Name])?.Value;\r
- return true;\r
- }\r
-\r
- public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)\r
- {\r
- result = _type == JsonType.Object && _dict.Contains(binder.Name);\r
- return true;\r
- }\r
-\r
- public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)\r
- {\r
- switch (_type)\r
- {\r
- case JsonType.Array:\r
- result = _array[(int)indexes[0]]?.Value;\r
- return true;\r
- case JsonType.Object:\r
- result = ((JsonObject)_dict[(string)indexes[0]])?.Value;\r
- return true;\r
- }\r
- result = null;\r
- return false;\r
- }\r
-\r
- public override bool TryConvert(ConvertBinder binder, out object result)\r
- {\r
- if (binder.Type == typeof(IEnumerable))\r
- {\r
- switch (_type)\r
- {\r
- case JsonType.Array:\r
- result = _array.Select(x => x.Value);\r
- return true;\r
- case JsonType.Object:\r
- result = _dict.Cast<DictionaryEntry>().Select(x => new KeyValuePair<string, dynamic>((string)x.Key, x.Value));\r
- return true;\r
- default:\r
- result = null;\r
- return false;\r
- }\r
- }\r
- if (_type != JsonType.Array)\r
- return ConvertPrivateType(binder.Type, out result);\r
- if (binder.Type == typeof(int[]))\r
- {\r
- result = _array.Select(x => (int)x._number).ToArray();\r
- return true;\r
- }\r
- if (binder.Type.IsArray)\r
- {\r
- return ConvertArray(binder.Type.GetElementType(), _array, out result);\r
- }\r
- result = null;\r
- return false;\r
- }\r
-\r
- private bool ConvertPrivateType(Type type, out object result)\r
- {\r
- if (type == typeof(bool) && _type == JsonType.Bool)\r
- {\r
- result = _bool;\r
- return true;\r
- }\r
- if (type == typeof(int) && _type == JsonType.Number)\r
- {\r
- result = (int)_number;\r
- return true;\r
- }\r
- if (type == typeof(double) && _type == JsonType.Number)\r
- {\r
- result = _number;\r
- return true;\r
- }\r
- if (type == typeof(string) && _type == JsonType.String)\r
- {\r
- result = _string;\r
- return true;\r
- }\r
- if (type == typeof(object))\r
- {\r
- result = Value;\r
- return true;\r
- }\r
- result = null;\r
- return false;\r
- }\r
-\r
- private bool ConvertArray(Type type, List<JsonObject> values, out object result)\r
- {\r
- result = null;\r
- var array = Array.CreateInstance(type, values.Count);\r
- for (var i = 0; i < array.Length; i++)\r
- {\r
- if (type.IsArray)\r
- {\r
- if (!values[i].IsArray || !ConvertArray(type.GetElementType(), values[i]._array, out var one))\r
- return false;\r
- array.SetValue((dynamic)one, i);\r
- }\r
- else\r
- {\r
- if (!values[i].ConvertPrivateType(type, out var one))\r
- return false;\r
- array.SetValue((dynamic)one, i);\r
- }\r
- }\r
- result = array;\r
- return true;\r
- }\r
-\r
- public static JsonObject CreateJsonObject(object value)\r
- {\r
- switch (value)\r
- {\r
- case string s:\r
- return new JsonObject(s);\r
- case int i:\r
- return new JsonObject(i);\r
- case double d:\r
- return new JsonObject(d);\r
- case JsonObject json:\r
- return json;\r
- case IEnumerable ary:\r
- return new JsonObject(ary.Cast<object>().Select(CreateJsonObject).ToList());\r
- case object obj:\r
- var dict = new OrderedDictionary();\r
- foreach (var prop in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))\r
- dict.Add(prop.Name, CreateJsonObject(prop.GetValue(obj)));\r
- return new JsonObject(dict);\r
- }\r
- return null;\r
- }\r
-\r
- public override bool TrySetMember(SetMemberBinder binder, object value)\r
- {\r
- if (_type != JsonType.Object)\r
- return false;\r
- _dict[binder.Name] = CreateJsonObject(value);\r
- return true;\r
- }\r
-\r
- public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)\r
- {\r
- if (_type == JsonType.Array)\r
- {\r
- _array[(int)indexes[0]] = CreateJsonObject(value);\r
- return true;\r
- }\r
- if (_type == JsonType.Object)\r
- {\r
- _dict[(string)indexes[0]] = CreateJsonObject(value);\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
- public override string ToString()\r
- {\r
- var sb = new StringBuilder();\r
- ConvertToString(sb);\r
- return sb.ToString();\r
- }\r
-\r
- private void ConvertToString(StringBuilder sb)\r
- {\r
- switch (_type)\r
- {\r
- case JsonType.Bool:\r
- sb.Append(_bool ? "true" : "false");\r
- break;\r
- case JsonType.Number:\r
- sb.Append(_number.ToString("G"));\r
- break;\r
- case JsonType.String:\r
- sb.Append("\"");\r
- sb.Append(HttpUtility.JavascriptStringEncode(_string));\r
- sb.Append("\"");\r
- break;\r
- case JsonType.Array:\r
- sb.Append("[");\r
- var delimiter = "";\r
- foreach (var val in _array)\r
- {\r
- sb.Append(delimiter);\r
- if (val == null)\r
- {\r
- sb.Append("null");\r
- }\r
- else\r
- {\r
- val.ConvertToString(sb);\r
- }\r
- delimiter = ",";\r
- }\r
- sb.Append("]");\r
- break;\r
- case JsonType.Object:\r
- sb.Append("{");\r
- delimiter = "";\r
- foreach (DictionaryEntry entry in _dict)\r
- {\r
- sb.Append(delimiter);\r
- sb.Append("\"");\r
- sb.Append(entry.Key);\r
- sb.Append("\":");\r
- if (entry.Value == null)\r
- {\r
- sb.Append("null");\r
- }\r
- else\r
- {\r
- ((JsonObject)entry.Value).ConvertToString(sb);\r
- }\r
- delimiter = ",";\r
- }\r
- sb.Append("}");\r
- break;\r
- }\r
- }\r
-\r
- private object Value\r
- {\r
- get\r
- {\r
- switch (_type)\r
- {\r
- case JsonType.Bool:\r
- return _bool;\r
- case JsonType.Number:\r
- return _number;\r
- case JsonType.String:\r
- return _string;\r
- }\r
- return this;\r
- }\r
- }\r
-\r
- private enum JsonType\r
- {\r
- Bool,\r
- Number,\r
- String,\r
- Array,\r
- Object\r
- }\r
- }\r
-\r
- public class JsonParserException : Exception\r
- {\r
- public JsonParserException(string message) : base(message)\r
- {\r
- }\r
- }\r
-}
\ No newline at end of file