From 6cb300a27e566114063b6370e48aa26c0ee2b9f8 Mon Sep 17 00:00:00 2001 From: DebugST <2212233137@qq.com> Date: Mon, 5 Aug 2024 13:20:50 +0800 Subject: [PATCH] version 3.0 --- Src/STLib.Json.Test/Program.cs | 157 +- Src/STLib.Json.Test/STJsonTestObject.cs | 5 +- Src/STLib.Json.Test/STLib.Json.Test.csproj | 10 +- Src/STLib.Json.sln | 15 +- Src/STLib.Json/Converter/ObjectToString.cs | 190 - Src/STLib.Json/Converter/StringToSTJson.cs | 161 - Src/STLib.Json/DevLog.txt | 13 +- .../Exceptions/STJsonCastException.cs | 16 - .../Exceptions/STJsonParseException.cs | 19 - .../Extensions/STJson.Static.Extensions.cs | 10 + .../STJsonAggregateException.cs | 0 Src/STLib.Json/Extensions/STJsonCreator.cs | 302 ++ .../STJsonExtensions.cs} | 897 ++-- Src/STLib.Json/JsonParser/STJson.DataType.cs | 47 - Src/STLib.Json/JsonParser/STJsonTokenizer.cs | 252 - .../STJsonPathBuildInFunctions.cs | 424 -- Src/STLib.Json/STJson.Static.cs | 308 -- Src/STLib.Json/STJson.cs | 463 -- .../Attributes/STJsonAttribute.cs | 4 +- .../Attributes/STJsonConverterAttribute.cs | 0 .../Attributes/STJsonPropertyAttribute.cs | 0 .../{ => STJson}/Converter/FPInfo.cs | 28 +- .../{ => STJson}/Converter/ObjectToSTJson.cs | 45 +- .../STJson/Converter/ObjectToString.cs | 397 ++ .../Converter/STJsonBuildInConverter.cs | 258 +- .../{ => STJson}/Converter/STJsonConverter.cs | 0 .../{ => STJson}/Converter/STJsonToObject.cs | 29 +- .../Entities/Enums/STJsonSerializeMode.cs | 7 + .../STJson/Entities/Enums/STJsonValueType.cs | 7 + .../Entities/Structures/STJsonTypeMapInfo.cs | 42 + .../STJson/Exceptions/STJsonCastException.cs | 11 + .../Exceptions/STJsonException.cs | 2 +- .../STJson/Exceptions/STJsonParseException.cs | 30 + .../Exceptions/STJsonWriterException.cs | 12 + .../Parser/Entities/Enums/STJsonTokenType.cs | 7 + .../Parser/Entities/Structures/STJsonToken.cs | 28 + Src/STLib.Json/STJson/Parser/STJsonParser.cs | 251 + .../STJson/Parser/STJsonTokenReader.cs | 316 ++ .../STJson/Parser/STJsonTokenizer.cs | 258 + .../STJson/Parser_backup/STJsonParserOld.cs | 229 + .../Parser_backup/STJsonTokenizerOld.cs | 201 + Src/STLib.Json/STJson/STJson.Static.cs | 453 ++ Src/STLib.Json/STJson/STJson.cs | 754 +++ Src/STLib.Json/STJson/STJsonReader.cs | 273 ++ Src/STLib.Json/STJson/STJsonReaderItem.cs | 92 + Src/STLib.Json/{ => STJson}/STJsonSetting.cs | 5 +- Src/STLib.Json/STJson/STJsonWriter.cs | 300 ++ .../Exceptions/STJsonPathException.cs | 0 .../Exceptions/STJsonPathParseException.cs | 0 .../STJsonPath.DataType.cs | 127 +- .../{ => STJsonPath}/STJsonPath.Static.cs | 94 +- Src/STLib.Json/{ => STJsonPath}/STJsonPath.cs | 27 +- .../STJsonPath/STJsonPathBuildInFunctions.cs | 531 ++ .../STJsonPathExpressParser.cs | 96 +- .../STJsonPathExpression.cs | 327 +- .../STJsonPathItem.cs | 0 .../STJsonPathParser.cs | 0 .../STJsonPathTokenizer.cs | 0 Src/STLib.Json/STLib.Json.csproj | 54 +- docs/index.html | 8 +- docs/tutorial_cn.html | 4309 +++++++++-------- docs/tutorial_cn.txt | 669 ++- docs/tutorial_en.html | 1034 ++-- docs/tutorial_en.txt | 795 ++- 64 files changed, 9765 insertions(+), 5634 deletions(-) delete mode 100644 Src/STLib.Json/Converter/ObjectToString.cs delete mode 100644 Src/STLib.Json/Converter/StringToSTJson.cs delete mode 100644 Src/STLib.Json/Exceptions/STJsonCastException.cs delete mode 100644 Src/STLib.Json/Exceptions/STJsonParseException.cs create mode 100644 Src/STLib.Json/Extensions/STJson.Static.Extensions.cs rename Src/STLib.Json/{ExtensionMethods => Extensions}/STJsonAggregateException.cs (100%) create mode 100644 Src/STLib.Json/Extensions/STJsonCreator.cs rename Src/STLib.Json/{ExtensionMethods/STJsonExtension.cs => Extensions/STJsonExtensions.cs} (53%) delete mode 100644 Src/STLib.Json/JsonParser/STJson.DataType.cs delete mode 100644 Src/STLib.Json/JsonParser/STJsonTokenizer.cs delete mode 100644 Src/STLib.Json/JsonPathParser/STJsonPathBuildInFunctions.cs delete mode 100644 Src/STLib.Json/STJson.Static.cs delete mode 100644 Src/STLib.Json/STJson.cs rename Src/STLib.Json/{ => STJson}/Attributes/STJsonAttribute.cs (53%) rename Src/STLib.Json/{ => STJson}/Attributes/STJsonConverterAttribute.cs (100%) rename Src/STLib.Json/{ => STJson}/Attributes/STJsonPropertyAttribute.cs (100%) rename Src/STLib.Json/{ => STJson}/Converter/FPInfo.cs (86%) rename Src/STLib.Json/{ => STJson}/Converter/ObjectToSTJson.cs (81%) create mode 100644 Src/STLib.Json/STJson/Converter/ObjectToString.cs rename Src/STLib.Json/{ => STJson}/Converter/STJsonBuildInConverter.cs (81%) rename Src/STLib.Json/{ => STJson}/Converter/STJsonConverter.cs (100%) rename Src/STLib.Json/{ => STJson}/Converter/STJsonToObject.cs (95%) create mode 100644 Src/STLib.Json/STJson/Entities/Enums/STJsonSerializeMode.cs create mode 100644 Src/STLib.Json/STJson/Entities/Enums/STJsonValueType.cs create mode 100644 Src/STLib.Json/STJson/Entities/Structures/STJsonTypeMapInfo.cs create mode 100644 Src/STLib.Json/STJson/Exceptions/STJsonCastException.cs rename Src/STLib.Json/{ => STJson}/Exceptions/STJsonException.cs (58%) create mode 100644 Src/STLib.Json/STJson/Exceptions/STJsonParseException.cs create mode 100644 Src/STLib.Json/STJson/Exceptions/STJsonWriterException.cs create mode 100644 Src/STLib.Json/STJson/Parser/Entities/Enums/STJsonTokenType.cs create mode 100644 Src/STLib.Json/STJson/Parser/Entities/Structures/STJsonToken.cs create mode 100644 Src/STLib.Json/STJson/Parser/STJsonParser.cs create mode 100644 Src/STLib.Json/STJson/Parser/STJsonTokenReader.cs create mode 100644 Src/STLib.Json/STJson/Parser/STJsonTokenizer.cs create mode 100644 Src/STLib.Json/STJson/Parser_backup/STJsonParserOld.cs create mode 100644 Src/STLib.Json/STJson/Parser_backup/STJsonTokenizerOld.cs create mode 100644 Src/STLib.Json/STJson/STJson.Static.cs create mode 100644 Src/STLib.Json/STJson/STJson.cs create mode 100644 Src/STLib.Json/STJson/STJsonReader.cs create mode 100644 Src/STLib.Json/STJson/STJsonReaderItem.cs rename Src/STLib.Json/{ => STJson}/STJsonSetting.cs (86%) create mode 100644 Src/STLib.Json/STJson/STJsonWriter.cs rename Src/STLib.Json/{ => STJsonPath}/Exceptions/STJsonPathException.cs (100%) rename Src/STLib.Json/{ => STJsonPath}/Exceptions/STJsonPathParseException.cs (100%) rename Src/STLib.Json/{JsonPathParser => STJsonPath}/STJsonPath.DataType.cs (56%) rename Src/STLib.Json/{ => STJsonPath}/STJsonPath.Static.cs (90%) rename Src/STLib.Json/{ => STJsonPath}/STJsonPath.cs (88%) create mode 100644 Src/STLib.Json/STJsonPath/STJsonPathBuildInFunctions.cs rename Src/STLib.Json/{JsonPathParser => STJsonPath}/STJsonPathExpressParser.cs (76%) rename Src/STLib.Json/{JsonPathParser => STJsonPath}/STJsonPathExpression.cs (55%) rename Src/STLib.Json/{JsonPathParser => STJsonPath}/STJsonPathItem.cs (100%) rename Src/STLib.Json/{JsonPathParser => STJsonPath}/STJsonPathParser.cs (100%) rename Src/STLib.Json/{JsonPathParser => STJsonPath}/STJsonPathTokenizer.cs (100%) diff --git a/Src/STLib.Json.Test/Program.cs b/Src/STLib.Json.Test/Program.cs index 74edf71..25ce952 100644 --- a/Src/STLib.Json.Test/Program.cs +++ b/Src/STLib.Json.Test/Program.cs @@ -1,74 +1,119 @@ -using System.Data; +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using STLib.Json; namespace STLib.Json.Test { - internal class Program + public class UserInfo { - [STJson(STJsonSerilizaMode.All)] - public class Student - { - [STJsonProperty("test_name")] - public string Name; - [STJsonProperty] - public int Age; - public Gender Gender; - [STJsonProperty] // optional - public List Hobby; - } + public string Name { get; set; } + public string Github { get; set; } + public string[] Language { get; set; } + public Address Address { get; set; } + } - public enum Gender - { - Male, Female - } + public class Address + { + public string Country { get; set; } + public string Province { get; set; } + public string City { get; set; } + } - static void Main(string[] args) { - DataTable dt = new DataTable(); - dt.Columns.Add("name"); - dt.Columns.Add("age", typeof(int)); - dt.Columns.Add("is_boy", typeof(bool)); - for (int i = 0; i < 10; i++) { - DataRow dr = dt.NewRow(); - dr["name"] = "test"; - dr["age"] = 10; - dr["is_boy"] = i % 2 == 0; - dt.Rows.Add(dr); - } + internal class Program + { + static void Main(string[] args) + { + //var obj_test = STJsonTestObject.CreateTestObject(); + var type = typeof(UserInfo); + var obj_test = new UserInfo() + { + Name = "DebugST", + Github = "https://github.com/DebugST", + Language = new string[] { "C#", "JS", "..." }, + Address = new Address() + { + Country = "China", + Province = "GuangDong", + City = "ShenZhen" + } + }; + string str_json, str_temp; + Console.WriteLine("========================================"); + Console.WriteLine(STJson.Serialize(obj_test)); + Console.WriteLine("========================================"); - var str = STJson.Serialize(dt, 4); - Console.WriteLine(str); + Console.WriteLine("[CHECK_IS_SAME]"); - var stu = new Student() { - Name = "Tom", - Age = 20, - Hobby = new List() { "sing", "dance" } - }; - object obj = STJsonTestObject.CreateTestObject(); - Console.WriteLine(STJson.Serialize(obj, 4)); + str_json = JsonConvert.SerializeObject(obj_test); + str_temp = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(str_json)); + Console.WriteLine("Newtonsoft : " + (str_temp == str_json)); - str = STJson.Serialize(stu, 4); - Console.WriteLine(str); - stu = STJson.Deserialize(str); + str_json = STJson.Serialize(obj_test); + str_temp = STJson.Serialize(STJson.Deserialize(str_json)); + Console.WriteLine("STJson : " + (str_temp == str_json)); - Console.WriteLine(STJson.Serialize(new System.Drawing.Rectangle(10, 10, 100, 100))); + var sw = new System.Diagnostics.Stopwatch(); - Console.WriteLine(STJson.FromObject(stu).Select("..").ToString(4)); + int n_counter = 50000; + System.Threading.Thread.Sleep(5000); + Console.WriteLine("========================================"); + Console.WriteLine("[Deserialize To Linq] - " + n_counter); + sw.Reset(); + sw.Start(); + for (int i = 0; i < n_counter; i++) { + JsonConvert.DeserializeObject(str_json); + } + sw.Stop(); + Console.WriteLine("Newstonoft : " + sw.ElapsedMilliseconds); - //str = STJson.Serialize(obj, 4); - //var sw = new System.Diagnostics.Stopwatch(); - //int nCount = 10000; + sw.Reset(); + sw.Start(); + for (int i = 0; i < n_counter; i++) { + STJson.Deserialize(str_json); + } + sw.Stop(); + Console.WriteLine("STJson : " + sw.ElapsedMilliseconds); + // ==================================================================================================== + System.Threading.Thread.Sleep(5000); + Console.WriteLine("========================================"); + Console.WriteLine("[Deserialize To object] - " + n_counter); + sw.Reset(); + sw.Start(); + for (int i = 0; i < n_counter; i++) { + JsonConvert.DeserializeObject(str_json); + } + sw.Stop(); + Console.WriteLine("Newstonoft : " + sw.ElapsedMilliseconds); - //while (true) { - // Console.ReadKey(); - // Console.WriteLine("==="); + sw.Reset(); + sw.Start(); + for (int i = 0; i < n_counter; i++) { + STJson.Deserialize(str_json); + } + sw.Stop(); + Console.WriteLine("STJson : " + sw.ElapsedMilliseconds); + // ==================================================================================================== + System.Threading.Thread.Sleep(5000); + Console.WriteLine("========================================"); + Console.WriteLine("[Serialize] - " + n_counter); + sw.Reset(); + sw.Start(); + for (int i = 0; i < n_counter; i++) { + JsonConvert.SerializeObject(obj_test); + } + sw.Stop(); + Console.WriteLine("Newstonoft : " + sw.ElapsedMilliseconds); - // sw.Restart(); - // for (int i = 0; i < nCount; i++) { + sw.Reset(); + sw.Start(); + for (int i = 0; i < n_counter; i++) { + STJson.Serialize(obj_test); + } + sw.Stop(); + Console.WriteLine("STJson : " + sw.ElapsedMilliseconds); - // var aaa = STJson.Deserialize(str); - // } - // sw.Stop(); - // Console.WriteLine(sw.ElapsedMilliseconds); - //} + Console.ReadKey(); } } } \ No newline at end of file diff --git a/Src/STLib.Json.Test/STJsonTestObject.cs b/Src/STLib.Json.Test/STJsonTestObject.cs index 8fc4db2..beb01d3 100644 --- a/Src/STLib.Json.Test/STJsonTestObject.cs +++ b/Src/STLib.Json.Test/STJsonTestObject.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace STLib.Json.Test { public enum TestObjectEnum { Enum1, Enum2, Enum3 } - [STJson(STJsonSerilizaMode.All)] + [STJson(STJsonSerializeMode.All)] public class TestObject { private string m_private_string = "private_string_obj"; diff --git a/Src/STLib.Json.Test/STLib.Json.Test.csproj b/Src/STLib.Json.Test/STLib.Json.Test.csproj index c8365a7..97e60be 100644 --- a/Src/STLib.Json.Test/STLib.Json.Test.csproj +++ b/Src/STLib.Json.Test/STLib.Json.Test.csproj @@ -1,14 +1,16 @@ - + Exe - net6.0 - enable - enable + net8.0 STJson DebugST + + + + diff --git a/Src/STLib.Json.sln b/Src/STLib.Json.sln index dab3cc1..12d5ca7 100644 --- a/Src/STLib.Json.sln +++ b/Src/STLib.Json.sln @@ -1,13 +1,16 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33424.131 +# Visual Studio 15 +VisualStudioVersion = 15.0.34930.48 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STLib.Json", "STLib.Json\STLib.Json.csproj", "{151B52D3-E5D0-4877-8A84-FF7764F270DD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STLib.Json.Test", "STLib.Json.Test\STLib.Json.Test.csproj", "{E9423C17-AF9B-46F4-BAE6-0D440F9D7095}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STLib.Json.Test", "STLib.Json.Test\STLib.Json.Test.csproj", "{E9423C17-AF9B-46F4-BAE6-0D440F9D7095}" EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -28,4 +31,10 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C0D3E6E9-5F55-4EC6-8578-F30C609492E6} EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/Src/STLib.Json/Converter/ObjectToString.cs b/Src/STLib.Json/Converter/ObjectToString.cs deleted file mode 100644 index 3ec85d8..0000000 --- a/Src/STLib.Json/Converter/ObjectToString.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System; -using System.Collections; -using System.Text; - -using ME = STLib.Json.ObjectToString; - -namespace STLib.Json -{ - internal class ObjectToString - { - private static Type m_type_json = typeof(STJson); - private static Type m_type_attr_stjson = typeof(STJsonAttribute); - - public static void Get(StringBuilder sb, object obj, STJsonSetting setting) { - if (obj == null) { - sb.Append("null"); - return; - } - var t = obj.GetType(); - if (t == m_type_json) { - sb.Append(obj.ToString()); - return; - } - bool bProcessed = true; - STJsonConverter converter = STJson.GetConverter(t); - if (converter != null) { - var str = converter.ObjectToString(t, obj, ref bProcessed); - if (bProcessed) { - sb.Append(str); - return; - } - } - if (t.IsEnum) { - if (setting.EnumUseNumber) { - sb.Append((Convert.ToInt64(obj)).ToString()); - } else { - sb.Append('\"'); - sb.Append(Convert.ToString(obj)); - sb.Append('\"'); - } - return; - } - - if (t.IsArray) { - Array arr = obj as Array; - var strName = t.FullName; - int nDim = strName.Length - strName.LastIndexOf('[') - 1; - int[] nLens = new int[nDim]; - int[] nIndices = new int[nDim]; - for (int i = 0; i < nDim; i++) { - nLens[i] = arr.GetLength(i); - } - ME.GetArray(sb, arr, nLens, nIndices, 0, setting); - return; - } - - if (t.IsGenericType) { - if (obj is IDictionary) { - var idic = (IDictionary)obj; - IEnumerator ie_keys = idic.Keys.GetEnumerator(); - IEnumerator ie_values = idic.Values.GetEnumerator(); - sb.Append('{'); - while (ie_keys.MoveNext() && ie_values.MoveNext()) { - var strKey = ie_keys.Current.ToString(); - sb.Append('\"'); - sb.Append(STJson.Escape(strKey)); - sb.Append("\":"); - ME.Get(sb, ie_values.Current, setting); - sb.Append(','); - } - ME.CheckEnd(sb, '}'); - return; - } - - if (obj is IEnumerable) { - IEnumerator ie = ((IEnumerable)obj).GetEnumerator(); - sb.Append('['); - while (ie.MoveNext()) { - ObjectToString.Get(sb, ie.Current, setting); - sb.Append(','); - } - ME.CheckEnd(sb, ']'); - return; - } - } - - var fps = FPInfo.GetFPInfo(t); - var serilizaModel = STJsonSerilizaMode.All; - if (!setting.IgnoreAttribute) { - //#if NETSTANDARD - // var attr = t.GetCustomAttribute(m_type_attr_stjson); - // if (attr != null) { - // serilizaModel = ((STJsonAttribute)attr).SerilizaModel; - // } - //#else - var attrs = t.GetCustomAttributes(m_type_attr_stjson, true); - if (attrs != null && attrs.Length > 0) { - serilizaModel = ((STJsonAttribute)attrs[0]).SerilizaMode; - } - //#endif - } - sb.Append('{'); - foreach (var p in fps) { - if (!p.CanGetValue) { - continue; - } - switch (serilizaModel) { - case STJsonSerilizaMode.All: - break; - case STJsonSerilizaMode.Include: - if (p.PropertyAttribute == null) { - continue; - } - break; - case STJsonSerilizaMode.Exclude: - if (p.PropertyAttribute != null) { - continue; - } - break; - } - switch (setting.KyeMode) { - case STJsonSetting.KeyMode.Include: - if (!setting.KeyList.Contains(p.KeyName)) { - continue; - } - break; - case STJsonSetting.KeyMode.Exclude: - if (setting.KeyList.Contains(p.KeyName)) { - continue; - } - break; - } - bProcessed = true; - converter = p.Converter; - if (converter == null) { - converter = STJson.GetCustomConverter(p.KeyName); - } - if (converter != null) { - var vul = p.GetValue(obj); - if (vul == null && setting.IgnoreNullValue) { - continue; - } - var str = converter.ObjectToString(t, vul, ref bProcessed); - if (bProcessed) { - sb.Append('\"'); - sb.Append(p.KeyName); - sb.Append("\":"); - sb.Append(str); - } - } else { - if (!p.CanGetValue) continue; - var vul = p.GetValue(obj); - if (vul == null && setting.IgnoreNullValue) { - continue; - } - sb.Append('\"'); - sb.Append(p.KeyName); - sb.Append("\":"); - ME.Get(sb, vul, setting); - } - sb.Append(','); - } - ME.CheckEnd(sb, '}'); - } - - private static void GetArray(StringBuilder sb, Array arr, int[] nLens, int[] nIndices, int nLevel, STJsonSetting setting) { - sb.Append('['); - for (int i = 0; i < nLens[nLevel]; i++) { - nIndices[nLevel] = i; - if (nLevel == nLens.Length - 1) { - var obj = arr.GetValue(nIndices); - ME.Get(sb, obj, setting); - } else { - ME.GetArray(sb, arr, nLens, nIndices, nLevel + 1, setting); - } - sb.Append(','); - } - ME.CheckEnd(sb, ']'); - } - - private static void CheckEnd(StringBuilder sb, char ch) { - if (sb[sb.Length - 1] == ',') { - sb[sb.Length - 1] = ch; - } else { - sb.Append(ch); - } - } - } -} - diff --git a/Src/STLib.Json/Converter/StringToSTJson.cs b/Src/STLib.Json/Converter/StringToSTJson.cs deleted file mode 100644 index 2fa20df..0000000 --- a/Src/STLib.Json/Converter/StringToSTJson.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections.Generic; - -using ME = STLib.Json.StringToSTJson; - -namespace STLib.Json -{ - internal class StringToSTJson - { - public static STJson Get(string strJson) { - var tokens = STJsonTokenizer.GetTokens(strJson); - if (tokens.Count == 0) { - throw new STJsonParseException(0, "Invalid string."); - } - int nIndex = 1; - if (tokens[0].Value == "{") { - return ME.GetObject(tokens, ref nIndex); - } - - if (tokens[0].Value == "[") { - return ME.GetArray(tokens, ref nIndex); - } - throw new STJsonParseException( - tokens[0].Index, - "Invalid char form index [" + tokens[0].Index + "]{" + tokens[0].Value + "}" - ); - } - - private static STJson GetObject(List tokens, ref int nIndex) { - STJson json = new STJson(); - json.SetModel(STJsonValueType.Object); - if (tokens[nIndex].Value == "}") { - nIndex++; - return json; - } - while (nIndex < tokens.Count) { - var token = tokens[nIndex++]; - if (token.Type != STJsonTokenizer.TokenType.String) { - throw new STJsonParseException( - token.Index, - "Invalid char form index [" + token.Index + "]{" + token.Value + "}" - ); - } - var jv = json.SetKey(token.Value); - token = tokens[nIndex++]; - if (token.Value != ":") { - throw new STJsonParseException( - token.Index, - "Invalid char form index [" + token.Index + "]{" + token.Value + "}" - ); - } - token = tokens[nIndex++]; - switch (token.Type) { - case STJsonTokenizer.TokenType.KeyWord: - switch (token.Value) { - case "true": - jv.SetValue(true); - break; - case "false": - jv.SetValue(false); - break; - case "null": - jv.SetValue(strText: null); - break; - } - break; - case STJsonTokenizer.TokenType.String: - jv.SetValue(token.Value); - break; - case STJsonTokenizer.TokenType.Long: - jv.SetValue(Convert.ToInt64(token.Value)); - break; - case STJsonTokenizer.TokenType.Double: - jv.SetValue(Convert.ToDouble(token.Value)); - break; - case STJsonTokenizer.TokenType.ObjectStart: - jv.SetValue(ME.GetObject(tokens, ref nIndex)); - break; - case STJsonTokenizer.TokenType.ArrayStart: - jv.SetValue(ME.GetArray(tokens, ref nIndex)); - break; - case STJsonTokenizer.TokenType.ObjectEnd: - return json; - default: - throw new STJsonParseException( - token.Index, - "Invalid char form index [" + token.Index + "]{" + token.Value + "}" - ); - } - token = tokens[nIndex++]; - switch (token.Value) { - case ",": continue; - case "}": return json; - default: - throw new STJsonParseException( - token.Index, - "Invalid char form index [" + token.Index + "]{" + token.Value + "}" - ); - } - } - throw new STJsonParseException(-1, "Incomplete string."); - } - - private static STJson GetArray(List smbs, ref int nIndex) { - STJson json = new STJson(); - json.SetModel(STJsonValueType.Array); - while (nIndex < smbs.Count) { - var token = smbs[nIndex++]; - switch (token.Type) { - case STJsonTokenizer.TokenType.String: - json.Append(token.Value); - break; - case STJsonTokenizer.TokenType.Long: - json.Append(Convert.ToInt64(token.Value)); - break; - case STJsonTokenizer.TokenType.Double: - json.Append(Convert.ToDouble(token.Value)); - break; - case STJsonTokenizer.TokenType.KeyWord: - switch (token.Value) { - case "true": - json.Append(true); - break; - case "false": - json.Append(false); - break; - case "null": - json.Append(json: null); - break; - } - break; - case STJsonTokenizer.TokenType.ObjectStart: - json.Append(ME.GetObject(smbs, ref nIndex)); - break; - case STJsonTokenizer.TokenType.ArrayStart: - json.Append(ME.GetArray(smbs, ref nIndex)); - break; - case STJsonTokenizer.TokenType.ArrayEnd: - return json; - default: - throw new STJsonParseException( - token.Index, - "Invalid char form index [" + token.Index + "]{" + token.Value + "}" - ); - } - token = smbs[nIndex++]; - switch (token.Value) { - case ",": continue; - case "]": return json; - default: - throw new STJsonParseException( - token.Index, - "Invalid char form index [" + token.Index + "]{" + token.Value + "}" - ); - } - } - throw new STJsonParseException(-1, "Incomplete string."); - } - } -} - diff --git a/Src/STLib.Json/DevLog.txt b/Src/STLib.Json/DevLog.txt index 661c0fa..a69e539 100644 --- a/Src/STLib.Json/DevLog.txt +++ b/Src/STLib.Json/DevLog.txt @@ -1,4 +1,15 @@ -Fixed JsonPath error for [?()] +[3.0.0][2024-08-02] +-------------------------------------------------- +Add Json5 support. +Add STJsonReader/Writer. +Add STJsonCreator. +Add STJson.ToString(TextWriter). +Optimize STJson.Sort(). +Optimize some STJsonPath built-in functions. + +[2.0.0][2024-02-14] +-------------------------------------------------- +Fixed JsonPath error for [?()] Fixed some string serialization cannot be parsed in other languages. [1.0.4][2023-11-27] diff --git a/Src/STLib.Json/Exceptions/STJsonCastException.cs b/Src/STLib.Json/Exceptions/STJsonCastException.cs deleted file mode 100644 index dc8a68d..0000000 --- a/Src/STLib.Json/Exceptions/STJsonCastException.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace STLib.Json -{ - public class STJsonCastException : InvalidCastException - { - public STJsonCastException(string strError) : base(strError) { } - public STJsonCastException(string strError, Exception innerException) : base(strError, innerException) { } - //public int Index { get; private set; } - - //public STJsonCastException(int nIndex, string strError) : base(strError) { - // this.Index = nIndex; - //} - } -} - diff --git a/Src/STLib.Json/Exceptions/STJsonParseException.cs b/Src/STLib.Json/Exceptions/STJsonParseException.cs deleted file mode 100644 index 12e34b5..0000000 --- a/Src/STLib.Json/Exceptions/STJsonParseException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace STLib.Json -{ - public class STJsonParseException : Exception - { - public int Index { get; private set; } - - public STJsonParseException(int nIndex, string strError) : base(strError) { - this.Index = nIndex; - } - - public STJsonParseException(int nIndex, char ch) - : base("Can not parse the string. Index: " + nIndex + ", char: [" + ch + "]") { - this.Index = nIndex; - } - } -} - diff --git a/Src/STLib.Json/Extensions/STJson.Static.Extensions.cs b/Src/STLib.Json/Extensions/STJson.Static.Extensions.cs new file mode 100644 index 0000000..96c01fd --- /dev/null +++ b/Src/STLib.Json/Extensions/STJson.Static.Extensions.cs @@ -0,0 +1,10 @@ +namespace STLib.Json +{ + public partial class STJson + { + public static STJson Create(STJsonCreatorStartCallback callback) + { + return new STJsonCreator().Create(callback); + } + } +} \ No newline at end of file diff --git a/Src/STLib.Json/ExtensionMethods/STJsonAggregateException.cs b/Src/STLib.Json/Extensions/STJsonAggregateException.cs similarity index 100% rename from Src/STLib.Json/ExtensionMethods/STJsonAggregateException.cs rename to Src/STLib.Json/Extensions/STJsonAggregateException.cs diff --git a/Src/STLib.Json/Extensions/STJsonCreator.cs b/Src/STLib.Json/Extensions/STJsonCreator.cs new file mode 100644 index 0000000..de988fc --- /dev/null +++ b/Src/STLib.Json/Extensions/STJsonCreator.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace STLib.Json +{ + public delegate void STJsonCreatorCallback(); + public delegate void STJsonCreatorForCallback(int n_index); + public delegate void STJsonCreatorForEachCallback(T v); + public delegate void STJsonCreatorStartCallback(STJsonCreator writer); + + public class STJsonCreatorException : STJsonException + { + public STJsonCreatorException(string str_error) : base(str_error) { } + } + + public class STJsonCreator + { + private bool m_b_started; + private STJson m_current_json; + + private Stack m_stack; + + public STJsonCreator() + { + m_stack = new Stack(); + } + + public STJson Create(STJsonCreatorStartCallback callback) + { + if (m_b_started) { + m_b_started = false; + throw new STJsonCreatorException("Please wait Create() end."); + } + m_stack.Clear(); + m_b_started = true; + this.Push(new STJson()); + callback(this); + m_b_started = false; + return m_current_json; + } + + public STJsonCreator SetItem(string str_key, long value) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, value); + return this; + } + + public STJsonCreator SetItem(string str_key, double value) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, value); + return this; + } + + public STJsonCreator SetItem(string str_key, bool value) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, value); + return this; + } + + public STJsonCreator SetItem(string str_key, string value) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, value); + return this; + } + + public STJsonCreator SetItem(string str_key, DateTime value) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, value); + return this; + } + + public STJsonCreator SetItem(string str_key, STJson json) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, json); + return this; + } + + public STJsonCreator SetItem(string str_key, object obj) + { + this.CheckStarted(); + m_current_json.SetItem(str_key, obj); + return this; + } + + public STJsonCreator SetItem(string str_key, bool b_express, STJsonCreatorCallback callback) + { + this.CheckStarted(); + if (!b_express) return this; + var json = new STJson(); + m_current_json.SetItem(str_key, json); + + this.Push(json); + callback(); + this.Pop(); + return this; + } + + public STJsonCreator Append(long value) + { + this.CheckStarted(); + m_current_json.Append(value); + return this; + } + + public STJsonCreator Append(double value) + { + this.CheckStarted(); + m_current_json.Append(value); + return this; + } + + public STJsonCreator Append(bool value) + { + this.CheckStarted(); + m_current_json.Append(value); + return this; + } + + public STJsonCreator Append(string value) + { + this.CheckStarted(); + m_current_json.Append(value); + return this; + } + + public STJsonCreator Append(DateTime value) + { + this.CheckStarted(); + m_current_json.Append(value); + return this; + } + + public STJsonCreator Append(object obj) + { + this.CheckStarted(); + m_current_json.Append(obj); + return this; + } + + public STJsonCreator Append(STJson json) + { + this.CheckStarted(); + m_current_json.Append(json); + return this; + } + + public STJsonCreator Append(STJsonCreatorCallback callback) + { + this.CheckStarted(); + var json = new STJson(); + m_current_json.Append(json); + + this.Push(json); + callback(); + this.Pop(); + return this; + } + + public STJsonCreator Append(int n_count, STJsonCreatorForCallback callback) + { + return this.Append(0, n_count, 1, callback); + } + + public STJsonCreator Append(int n_start, int n_end, STJsonCreatorForCallback callback) + { + return this.Append(n_start, n_end, 1, callback); + } + + public STJsonCreator Append(int n_start, int n_end, int n_step, STJsonCreatorForCallback callback) + { + this.CheckStarted(); + for (int i = n_start; i < n_end; i += n_step) { + var json = new STJson(); + m_current_json.Append(json); + + this.Push(json); + callback(i); + this.Pop(); + } + return this; + } + + public STJsonCreator Append(Array arr, STJsonCreatorForEachCallback callback) + { + + return this.Append(arr.GetEnumerator(), callback); + } + + public STJsonCreator Append(IList lst, STJsonCreatorForEachCallback callback) + { + + return this.Append(lst.GetEnumerator(), callback); + } + + public STJsonCreator Append(IList lst, STJsonCreatorForEachCallback callback) + { + + return this.Append(lst.GetEnumerator(), callback); + } + + public STJsonCreator Append(IEnumerator enumerator, STJsonCreatorForEachCallback callback) + { + return this.Append(enumerator, callback); + } + + public STJsonCreator Append(IEnumerator enumerator, STJsonCreatorForEachCallback callback) + { + this.CheckStarted(); + while (enumerator.MoveNext()) { + var json = new STJson(); + m_current_json.Append(json); + + this.Push(json); + callback(enumerator.Current); + this.Pop(); + } + return this; + } + + public STJsonCreator Append(IEnumerable enumerable, STJsonCreatorForEachCallback callback) + { + return this.Append(enumerable, callback); + } + + public STJsonCreator Append(IEnumerable enumerable, STJsonCreatorForEachCallback callback) + { + this.CheckStarted(); + foreach (var v in enumerable) { + var json = new STJson(); + m_current_json.Append(json); + + this.Push(json); + callback(v); + this.Pop(); + } + return this; + } + + public STJsonCreator Set(string str_path, object obj) + { + this.CheckStarted(); + m_current_json.Set(str_path, obj); + return this; + } + + public STJsonCreator Set(string str_path, STJson json) + { + this.CheckStarted(); + m_current_json.Set(str_path, json); + return this; + } + + public STJsonCreator Set(string str_path, STJsonCreatorCallback callback) + { + return this.Set(str_path, true, callback); + } + + public STJsonCreator Set(string str_path, bool b_express, STJsonCreatorCallback callback) + { + this.CheckStarted(); + if (!b_express) return this; + var json = new STJson(); + m_current_json.Set(str_path, json); + + this.Push(json); + callback(); + this.Pop(); + return this; + } + + + private void Push(STJson json) + { + m_current_json = json; + m_stack.Push(json); + } + + private void Pop() + { + m_current_json = null; + if (m_stack.Count == 0) return; + m_stack.Pop(); + if (m_stack.Count == 0) return; + m_current_json = m_stack.Peek(); + } + + private void CheckStarted() + { + if (m_b_started) { + return; + } + throw new STJsonCreatorException("Please call Create() first."); + } + } +} diff --git a/Src/STLib.Json/ExtensionMethods/STJsonExtension.cs b/Src/STLib.Json/Extensions/STJsonExtensions.cs similarity index 53% rename from Src/STLib.Json/ExtensionMethods/STJsonExtension.cs rename to Src/STLib.Json/Extensions/STJsonExtensions.cs index 9ac35bd..44a5f4b 100644 --- a/Src/STLib.Json/ExtensionMethods/STJsonExtension.cs +++ b/Src/STLib.Json/Extensions/STJsonExtensions.cs @@ -1,439 +1,292 @@ using System; using System.Collections.Generic; -using ME = STLib.Json.STJsonExtension; +using ME = STLib.Json.STJsonExtensions; namespace STLib.Json { - public static class STJsonExtension + public static class STJsonExtensions { - private struct MergeSortRange - { - public int Left; - public int Right; - } + public delegate int STJsonSortCallback(STJson json_a, STJson json_b); + public delegate void STJsonForEachCallback(STJson json); - private struct MergeSortInfo - { - public int Index; - public double[] Values; - - //public override string ToString() { - // return "[" + Index + "] - {" + String.Join(",", Values) + "}"; - //} - } + #region Bind Object - public static string GetValue(this STJson json) { - if (json.Value == null) { - return null; - } - return json.Value.ToString(); - } + //private static Type m_type_string = typeof(string); - public static string GetValue(this STJson json, string strJsonPath) { - string strResult = null; - json.GetValue(new STJsonPath(strJsonPath), null, out strResult); - return strResult; - } + //public static STJson ToSTJson(this object obj) + //{ + // var type = obj.GetType(); + // if (type == m_type_string) { + // return STJson.Deserialize(obj.ToString()); + // } + // return STJson.FromObject(obj); + //} - public static string GetValue(this STJson json, STJsonPath jsonPath) { - string strResult = null; - json.GetValue(jsonPath, null, out strResult); - return strResult; - } + //public static string ToSTJsonString(this object obj) + //{ + // return STJson.Serialize(obj, STJsonSetting.Default); + //} - public static string GetValue(this STJson json, string strJsonPath, string defaltValue) { - string strResult = null; - json.GetValue(new STJsonPath(strJsonPath), defaltValue, out strResult); - return strResult; - } + //public static string ToSTJsonString(this object obj, int n_space_count) + //{ + // var type = obj.GetType(); + // if (type == m_type_string) { + // return STJson.Deserialize(obj.ToString()).ToString(n_space_count); + // } + // return STJson.Serialize(obj, n_space_count); + //} - public static string GetValue(this STJson json, STJsonPath jsonPath, string defaltVale) { - string strResult = null; - json.GetValue(jsonPath, defaltVale, out strResult); - return strResult; - } + //public static string ToSTJsonString(this object obj, int n_space_count, STJsonSetting setting) + //{ + // return STJson.Serialize(obj, n_space_count, setting); + //} - public static bool GetValue(this STJson json, string strJsonPath, out string strResult) { - return json.GetValue(new STJsonPath(strJsonPath), null, out strResult); - } + #endregion Bind - public static bool GetValue(this STJson json, STJsonPath jsonPath, out string strResult) { - return json.GetValue(jsonPath, null, out strResult); + public static bool IsNullOrNullValue(this STJson json) + { + return json == null || json.IsNullValue; } - public static bool GetValue(this STJson json, string strJsonPath, string defaultValue, out string strResult) { - return json.GetValue(new STJsonPath(strJsonPath), defaultValue, out strResult); + public static bool IsNullOrNullValue(this STJson json, string str_path) + { + return json.IsNullOrNullValue(new STJsonPath(str_path)); } - public static bool GetValue(this STJson json, STJsonPath jsonPath, string defaultValue, out string result) { - result = defaultValue; - var j = jsonPath.SelectFirst(json); - if (j == null) { - return false; - } - if (j.Value != null) { - result = j.Value.ToString(); - } else { - result = null; + public static bool IsNullOrNullValue(this STJson json, STJsonPath json_path) + { + if (json == null || json.IsNullValue) { + return true; } - return true; + var j = json_path.SelectFirst(json); + return j == null || j.IsNullValue; } - // ================================================================================================== - - public static T GetValue(this STJson json) { - if (json.Value == null) { - return default(T); - } - var t = typeof(T); - bool bProcessed = true; - var convert = STJsonBuildInConverter.Get(t); - if (convert != null) { - var value = convert.JsonToObject(t, json, ref bProcessed); - if (bProcessed) { - return (T)value; - } + public static STJson ForEach(this STJson json, STJsonForEachCallback callback) + { + if (json == null) return json; + foreach (var j in json) { + callback(j); } - return (T)json.Value; + return json; } - public static T GetValue(this STJson json, T defaultValue) { - if (json.Value == null) { - return defaultValue; - } - var t = typeof(T); - try { - bool bProcessed = true; - var convert = STJsonBuildInConverter.Get(t); - if (convert != null) { - var value = convert.JsonToObject(t, json, ref bProcessed); - if (bProcessed) { - return (T)value; - } - } - return (T)json.Value; - } catch { - return defaultValue; - } + public static string GetValue(this STJson json, string str_path) + { + var json_path = new STJsonPath(str_path); + var j = json.SelectFirst(json_path); + return j.GetValue(); } - public static bool GetValue(this STJson json, out T result) { - if (json.Value == null) { - result = default(T); - return false; - } - try { - var t = typeof(T); - bool bProcessed = true; - var convert = STJsonBuildInConverter.Get(t); - if (convert != null) { - var value = convert.JsonToObject(t, json, ref bProcessed); - if (bProcessed) { - result = (T)value; - return true; - } - } - result = (T)json.Value; - return true; - } catch { - result = default(T); - return false; - } + public static string GetValue(this STJson json, STJsonPath json_path) + { + var j = json.SelectFirst(json_path); + return j.GetValue(); } - public static T GetValue(this STJson json, string strJsonPath) { - return json.GetValue(new STJsonPath(strJsonPath)); + // ================================================================================================== + + public static T GetValue(this STJson json, string str_path) + { + return json.GetValue(new STJsonPath(str_path)); } - public static T GetValue(this STJson json, STJsonPath jsonPath) { - var j = jsonPath.SelectFirst(json); + public static T GetValue(this STJson json, STJsonPath json_path) + { + var j = json_path.SelectFirst(json); if (j == null) { - throw new STJsonPathException("Can not selected a object with path {" + jsonPath.SourceText + "}"); + throw new STJsonPathException("Can not selected a object with path {" + json_path.SourceText + "}"); } + return json.GetValue(); + /* var t = typeof(T); - bool bProcessed = true; + bool b_processed = true; var convert = STJsonBuildInConverter.Get(t); if (convert != null) { - var value = convert.JsonToObject(t, j, ref bProcessed); - if (bProcessed) { + var value = convert.JsonToObject(t, j, ref b_processed); + if (b_processed) { return (T)value; } } - return STJson.Deserialize(j); - //return (T)j.Value; + return STJson.Deserialize(j);*/ } - public static T GetValue(this STJson json, string strJsonPath, T defaultValue) { - return json.GetValue(new STJsonPath(strJsonPath), defaultValue); + public static T GetValue(this STJson json, string str_path, T default_value) + { + return json.GetValue(new STJsonPath(str_path), default_value); } - public static T GetValue(this STJson json, STJsonPath jsonPath, T defaultValue) { - T reslt; - json.GetValue(jsonPath, defaultValue, out reslt); - return reslt; + public static T GetValue(this STJson json, STJsonPath json_path, T default_value) + { + T result; + json.GetValue(json_path, default_value, out result); + return result; } - public static bool GetValue(this STJson json, string strJsonPath, out T result) { - return json.GetValue(new STJsonPath(strJsonPath), default(T), out result); + public static bool GetValue(this STJson json, string str_path, out T result) + { + return json.GetValue(new STJsonPath(str_path), default(T), out result); } - public static bool GetValue(this STJson json, STJsonPath jsonPath, out T result) { - return json.GetValue(jsonPath, default(T), out result); + public static bool GetValue(this STJson json, STJsonPath json_path, out T result) + { + return json.GetValue(json_path, default(T), out result); } - public static bool GetValue(this STJson json, string strJsonPath, T defaultValue, out T result) { - return json.GetValue(new STJsonPath(strJsonPath), defaultValue, out result); + public static bool GetValue(this STJson json, string str_path, T default_value, out T result) + { + return json.GetValue(new STJsonPath(str_path), default_value, out result); } - public static bool GetValue(this STJson json, STJsonPath jsonPath, T defaultValue, out T result) { - result = defaultValue; - var j = jsonPath.SelectFirst(json); - if (j == null) { - return false; - } - if (j.Value == null) { + public static bool GetValue(this STJson json, STJsonPath json_path, T default_value, out T result) + { + result = default_value; + if (json == null) return false; + var j = json_path.SelectFirst(json); + if (j == null || j.Value == null) { return false; } - try { - var t = typeof(T); - bool bProcessed = true; - var convert = STJsonBuildInConverter.Get(t); - if (convert != null) { - var value = convert.JsonToObject(t, j, ref bProcessed); - if (bProcessed) { - result = (T)value; - return true; - } - } - result = STJson.Deserialize(j);// (T)j.Value; + if (j.GetValue(out result)) { return true; - } catch { - return false; } + result = default_value; + return false; } // ================================================================================================== - public static STJson Set(this STJson json, string strJsonPath, string value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, int value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, long value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, float value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, double value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, bool value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, DateTime value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, STJson value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, string strJsonPath, object value) { - var jsonItemWithPath = new STJsonPath(strJsonPath).CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - - public static STJson Set(this STJson json, STJsonPath jsonPath, string value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, int value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, long value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, float value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, double value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, bool value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, DateTime value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); - return json; - } - - public static STJson Set(this STJson json, STJsonPath jsonPath, STJson value) { - var jsonItemWithPath = jsonPath.CreatePathJson(value); - STJsonPath.RestorePathJson(json, jsonItemWithPath); + public static STJson Set(this STJson json, string str_path, object value) + { + var json_item_with_path = new STJsonPath(str_path).CreatePathJson(STJson.FromObject(value)); + STJsonPath.RestorePathJson(json, json_item_with_path); return json; } - public static STJson Set(this STJson json, STJsonPath jsonPath, object value) { - var jsonItemWithPath = jsonPath.CreatePathJson(STJson.FromObject(value)); - STJsonPath.RestorePathJson(json, jsonItemWithPath); + public static STJson Set(this STJson json, STJsonPath json_path, object value) + { + var json_item_with_path = json_path.CreatePathJson(STJson.FromObject(value)); + STJsonPath.RestorePathJson(json, json_item_with_path); return json; } - public static STJson Set(this STJson json, STJsonPath jsonPath, STJsonPathCallBack callBack) { - var jsonItemWithPath = jsonPath.CreatePathJson(callBack); - STJsonPath.RestorePathJson(json, jsonItemWithPath); + public static STJson Set(this STJson json, STJsonPath json_path, STJsonPathCallBack callback) + { + var json_item_with_path = json_path.CreatePathJson(callback); + STJsonPath.RestorePathJson(json, json_item_with_path); return json; } // ================================================================================================== - public static STJson Select(this STJson json, string strJsonPath) { - return new STJsonPath(strJsonPath).Select(json, STJsonPathSelectMode.ItemOnly, null); + public static STJson Select(this STJson json, string str_path) + { + return new STJsonPath(str_path).Select(json, STJsonPathSelectMode.ItemOnly, null); } - public static STJson Select(this STJson json, string strJsonPath, STJsonPathCallBack callBack) { - return new STJsonPath(strJsonPath).Select(json, STJsonPathSelectMode.ItemOnly, callBack); + public static STJson Select(this STJson json, string str_path, STJsonPathCallBack callback) + { + return new STJsonPath(str_path).Select(json, STJsonPathSelectMode.ItemOnly, callback); } - //public static STJson Select(this STJson json, string strJsonPath, STJsonPathCallBackVoid callBack) { - // return new STJsonPath(strJsonPath).Select(json, STJsonPathSelectMode.ItemOnly, callBack); - //} - - public static STJson Select(this STJson json, string strJsonPath, STJsonPathSelectMode model) { - return new STJsonPath(strJsonPath).Select(json, model, null); + public static STJson Select(this STJson json, string str_path, STJsonPathSelectMode model) + { + return new STJsonPath(str_path).Select(json, model, null); } - public static STJson Select(this STJson json, string strJsonPath, STJsonPathSelectMode model, STJsonPathCallBack callBack) { - return new STJsonPath(strJsonPath).Select(json, model, callBack); + public static STJson Select(this STJson json, string str_path, STJsonPathSelectMode model, STJsonPathCallBack callback) + { + return new STJsonPath(str_path).Select(json, model, callback); } - //public static STJson Select(this STJson json, string strJsonPath, STJsonPathSelectMode model, STJsonPathCallBackVoid callBack) { - // return new STJsonPath(strJsonPath).Select(json, model, callBack); - //} - - public static STJson Select(this STJson json, STJsonPath jsonPath) { - return jsonPath.Select(json, STJsonPathSelectMode.ItemWithPath, null); + public static STJson Select(this STJson json, STJsonPath json_path) + { + return json_path.Select(json, STJsonPathSelectMode.ItemWithPath, null); } - public static STJson Select(this STJson json, STJsonPath jsonPath, STJsonPathCallBack callBack) { - return jsonPath.Select(json, STJsonPathSelectMode.ItemOnly, callBack); + public static STJson Select(this STJson json, STJsonPath json_path, STJsonPathCallBack callBack) + { + return json_path.Select(json, STJsonPathSelectMode.ItemOnly, callBack); } - //public static STJson Select(this STJson json, STJsonPath jsonPath, STJsonPathCallBackVoid callBack) { - // return jsonPath.Select(json, STJsonPathSelectMode.ItemOnly, callBack); - //} - - public static STJson Select(this STJson json, STJsonPath jsonPath, STJsonPathSelectMode model) { - return jsonPath.Select(json, model, null); + public static STJson Select(this STJson json, STJsonPath json_path, STJsonPathSelectMode model) + { + return json_path.Select(json, model, null); } - public static STJson Select(this STJson json, STJsonPath jsonPath, STJsonPathSelectMode model, STJsonPathCallBack callBack) { - return jsonPath.Select(json, model, callBack); + public static STJson Select(this STJson json, STJsonPath json_path, STJsonPathSelectMode model, STJsonPathCallBack callback) + { + return json_path.Select(json, model, callback); } - //public static STJson Select(this STJson json, STJsonPath jsonPath, STJsonPathSelectMode model, STJsonPathCallBackVoid callBack) { - // return jsonPath.Select(json, model, callBack); - //} - + // ============================================================================= - public static STJson SelectFirst(this STJson json, string strJsonPath) { - return new STJsonPath(strJsonPath).SelectFirst(json); + public static STJson SelectFirst(this STJson json, string str_path) + { + return new STJsonPath(str_path).SelectFirst(json); } - public static STJson SelectFirst(this STJson json, string strJsonPath, STJsonPathSelectMode model) { - return new STJsonPath(strJsonPath).SelectFirst(json, model); + public static STJson SelectFirst(this STJson json, string str_path, STJsonPathSelectMode model) + { + return new STJsonPath(str_path).SelectFirst(json, model); } - public static STJson SelectLast(this STJson json, string strJsonPath) { - return new STJsonPath(strJsonPath).SelectLast(json); + public static STJson SelectLast(this STJson json, string str_path) + { + return new STJsonPath(str_path).SelectLast(json); } - public static STJson SelectLast(this STJson json, string strJsonPath, STJsonPathSelectMode model) { - return new STJsonPath(strJsonPath).SelectLast(json, model); + public static STJson SelectLast(this STJson json, string str_path, STJsonPathSelectMode model) + { + return new STJsonPath(str_path).SelectLast(json, model); } - public static STJson SelectFirst(this STJson json, STJsonPath jsonPath) { - return jsonPath.SelectFirst(json); + public static STJson SelectFirst(this STJson json, STJsonPath json_path) + { + return json_path.SelectFirst(json); } - public static STJson SelectFirst(this STJson json, STJsonPath jsonPath, STJsonPathSelectMode model) { - return jsonPath.SelectFirst(json, model); + public static STJson SelectFirst(this STJson json, STJsonPath json_path, STJsonPathSelectMode model) + { + return json_path.SelectFirst(json, model); } - public static STJson SelectLast(this STJson json, STJsonPath jsonPath) { - return jsonPath.SelectLast(json); + public static STJson SelectLast(this STJson json, STJsonPath json_path) + { + return json_path.SelectLast(json); } - public static STJson SelectLast(this STJson json, STJsonPath jsonPath, STJsonPathSelectMode model) { - return jsonPath.SelectLast(json, model); + public static STJson SelectLast(this STJson json, STJsonPath json_path, STJsonPathSelectMode model) + { + return json_path.SelectLast(json, model); } // ================================================================================================== - public static STJson Group(this STJson json, params string[] strPaths) { - return json.Group(ME.StrToPath(strPaths)); + public static STJson Group(this STJson json, params string[] arr_str_path) + { + return json.Group(ME.StrToPath(arr_str_path)); } - public static STJson Group(this STJson json, params STJsonPath[] paths) { - ME.CheckPathsName(paths); + public static STJson Group(this STJson json, params STJsonPath[] arr_json_path) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + ME.CheckPathsName(arr_json_path); var json_ret = new STJson(); - var lst_dic = new List>>(); - foreach (var v in paths) { - lst_dic.Add(new Dictionary>()); + var lst_dic = new List>(); + foreach (var v in arr_json_path) { + lst_dic.Add(new Dictionary()); } object obj_temp = null; foreach (var v in json) { if (v.ValueType != STJsonValueType.Object) { continue; } - for (int i = 0; i < paths.Length; i++) { + for (int i = 0; i < arr_json_path.Length; i++) { var dic = lst_dic[i]; - var json_temp = paths[i].SelectFirst(v); + var json_temp = arr_json_path[i].SelectFirst(v); if (json_temp == null) { continue; } @@ -445,15 +298,14 @@ public static STJson Group(this STJson json, params STJsonPath[] paths) { case STJsonValueType.Datetime: obj_temp = json_temp.Value; break; - } if (obj_temp == null) { continue; } if (!dic.ContainsKey(obj_temp)) { - dic.Add(obj_temp, new List()); + dic.Add(obj_temp, STJson.CreateArray()); } - dic[obj_temp].Add(v); + dic[obj_temp].Append(v); } } for (int i = 0; i < lst_dic.Count; i++) { @@ -461,17 +313,19 @@ public static STJson Group(this STJson json, params STJsonPath[] paths) { foreach (var v in lst_dic[i]) { json_group.Append(new STJson() .SetItem("value", v.Key) + .SetItem("count", v.Value.Count) .SetItem("items", v.Value) ); } - json_ret.SetItem(paths[i].Name, json_group); + json_ret.SetItem(arr_json_path[i].Name, json_group); } return json_ret; } // ================================================================================================== - public static STJson Terms(this STJson json) { + public static STJson Terms(this STJson json) + { List lst = new List(); foreach (var v in json) { ME.GetTermsValue(v, lst); @@ -491,20 +345,25 @@ public static STJson Terms(this STJson json) { return json_ret; } - public static STJson Terms(this STJson json, params string[] strPaths) { - return json.Terms(ME.StrToPath(strPaths)); + public static STJson Terms(this STJson json, params string[] arr_str_path) + { + return json.Terms(ME.StrToPath(arr_str_path)); } - public static STJson Terms(this STJson json, params STJsonPath[] paths) { - ME.CheckPathsName(paths); + public static STJson Terms(this STJson json, params STJsonPath[] arr_json_path) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + ME.CheckPathsName(arr_json_path); STJson json_ret = new STJson(); - List[] lsts = new List[paths.Length]; - for (int i = 0; i < paths.Length; i++) { + List[] lsts = new List[arr_json_path.Length]; + for (int i = 0; i < arr_json_path.Length; i++) { lsts[i] = new List(); } foreach (var v in json) { - for (int i = 0; i < paths.Length; i++) { - var temp = paths[i].SelectFirst(v); + for (int i = 0; i < arr_json_path.Length; i++) { + var temp = arr_json_path[i].SelectFirst(v); if (temp == null) { continue; } @@ -512,7 +371,7 @@ public static STJson Terms(this STJson json, params STJsonPath[] paths) { } } Dictionary dic = new Dictionary(); - for (int i = 0; i < paths.Length; i++) { + for (int i = 0; i < arr_json_path.Length; i++) { dic.Clear(); foreach (var v in lsts[i]) { if (!dic.ContainsKey(v)) { @@ -525,23 +384,24 @@ public static STJson Terms(this STJson json, params STJsonPath[] paths) { foreach (var v in dic) { json_terms.Append(new STJson().SetItem("value", v.Key).SetItem("count", v.Value)); } - json_ret.SetItem(paths[i].Name, json_terms); + json_ret.SetItem(arr_json_path[i].Name, json_terms); } return json_ret; } - private static void GetTermsValue(STJson json, List lstResult) { + private static void GetTermsValue(STJson json, List lst_result) + { switch (json.ValueType) { case STJsonValueType.Boolean: case STJsonValueType.String: case STJsonValueType.Long: case STJsonValueType.Double: case STJsonValueType.Datetime: - lstResult.Add(json.Value); + lst_result.Add(json.Value); return; case STJsonValueType.Array: foreach (var v in json) { - ME.GetTermsValue(v, lstResult); + ME.GetTermsValue(v, lst_result); } return; } @@ -549,18 +409,224 @@ private static void GetTermsValue(STJson json, List lstResult) { // ================================================================================================== - public static STJson Sort(this STJson json) { + private struct MergeSortRange + { + public int Left; + public int Right; + } + + private static STJsonSortCallback BuildDefaultSortCallback(bool is_desc) + { + return (a, b) => + { + int n_ret = 0; + if (a.IsNullOrNullValue()) { + n_ret = -1; + } else if (b.IsNullOrNullValue()) { + n_ret = 1; + } else if (a.IsNumber && b.IsNumber) { + n_ret = Convert.ToDouble(a.Value) < Convert.ToDouble(b.Value) ? -1 : 1; + } else if (a.ValueType != b.ValueType) { + n_ret = a.ValueType - b.ValueType; + } else { + switch (a.ValueType) { + case STJsonValueType.Boolean: + n_ret = (bool)b.Value ? -1 : 1; + break; + case STJsonValueType.Datetime: + n_ret = (DateTime)a.Value < (DateTime)b.Value ? -1 : 1; + break; + case STJsonValueType.String: + n_ret = string.Compare(a.Value.ToString(), b.Value.ToString()); + break; + } + } + return is_desc ? -n_ret : n_ret; + }; + } + + private static STJsonSortCallback m_sort_callback_desc = ME.BuildDefaultSortCallback(true); + private static STJsonSortCallback m_sort_callback_asc = ME.BuildDefaultSortCallback(false); + + public static STJson Sort(this STJson json, bool is_desc = false, bool is_new_instance = false) + { + return json.Sort(null, is_new_instance, is_desc ? m_sort_callback_desc : m_sort_callback_asc); + } + + public static STJson Sort(this STJson json, string str_path, bool is_desc = false, bool is_new_instance = false) + { + return json.Sort(new STJsonPath(str_path), is_new_instance, is_desc ? m_sort_callback_desc : m_sort_callback_asc); + } + + public static STJson Sort(this STJson json, STJsonPath json_path, bool is_desc = false, bool is_new_instance = false) + { + return json.Sort(json_path, is_new_instance, is_desc ? m_sort_callback_desc : m_sort_callback_asc); + } + + public static STJson Sort(this STJson json, STJsonSortCallback callback) + { + return json.Sort(null, false, callback); + } + + public static STJson Sort(this STJson json, bool is_new_instance, STJsonSortCallback callback) + { + return json.Sort(null, is_new_instance, callback); + } + + private static STJson Sort(this STJson json, STJsonPath json_path, bool is_new_instance, STJsonSortCallback callback) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + if (!is_new_instance) { + ME.MergeSortSplit(json, json_path, callback, new STJson[json.Count], 0, json.Count - 1); + return json; + } + + int[] arr_index_src = new int[json.Count]; + for (int i = 0, n_len = arr_index_src.Length; i < n_len; i++) { + arr_index_src[i] = i; + } + ME.MergeSortSplit(json, json_path, callback, arr_index_src, new int[arr_index_src.Length], 0, json.Count - 1); + + STJson json_ret = STJson.CreateArray(); + foreach (var v in arr_index_src) { + json_ret.Append(json[v]); + } + return json_ret; + } + + private static void MergeSortSplit(STJson json, STJsonPath json_path, STJsonSortCallback callback, int[] arr_index_src, int[] arr_index_temp, int n_left, int n_right) + { + int n_mid = (n_left + n_right) >> 1; + if (n_left >= n_right) { + return; + } + var range_l = new MergeSortRange() { Left = n_left, Right = n_mid }; + var range_r = new MergeSortRange() { Left = n_mid + 1, Right = n_right }; + ME.MergeSortSplit(json, json_path, callback, arr_index_src, arr_index_temp, n_left, n_mid); + ME.MergeSortSplit(json, json_path, callback, arr_index_src, arr_index_temp, n_mid + 1, n_right); + ME.MergeSortMerge(json, json_path, callback, arr_index_src, arr_index_temp, range_l, range_r); + } + + private static void MergeSortMerge(STJson json, STJsonPath json_path, STJsonSortCallback callback, int[] arr_index_src, int[] arr_index_temp, MergeSortRange range_l, MergeSortRange range_r) + { + int n_index = 0; + int n_l_index = range_l.Left; + int n_r_index = range_r.Left; + if (json_path != null) { + while (n_l_index <= range_l.Right && n_r_index <= range_r.Right) { + var json_a = json_path.SelectFirst(json[arr_index_src[n_l_index]]); + var json_b = json_path.SelectFirst(json[arr_index_src[n_r_index]]); + if (callback(json_a, json_b) > 0) { + arr_index_temp[n_index++] = arr_index_src[n_r_index++]; + } else { + arr_index_temp[n_index++] = arr_index_src[n_l_index++]; + } + } + } else { + while (n_l_index <= range_l.Right && n_r_index <= range_r.Right) { + var json_a = json[arr_index_src[n_l_index]]; + var json_b = json[arr_index_src[n_r_index]]; + if (callback(json_a, json_b) > 0) { + arr_index_temp[n_index++] = arr_index_src[n_r_index++]; + } else { + arr_index_temp[n_index++] = arr_index_src[n_l_index++]; + } + } + } + while (n_l_index <= range_l.Right) { + arr_index_temp[n_index++] = arr_index_src[n_l_index++]; + } + while (n_r_index <= range_r.Right) { + arr_index_temp[n_index++] = arr_index_src[n_r_index++]; + } + for (int i = range_l.Left, j = 0; i <= range_r.Right; i++, j++) { + arr_index_src[i] = arr_index_temp[j]; + } + } + + private static void MergeSortSplit(STJson json, STJsonPath json_path, STJsonSortCallback callback, STJson[] arr_json_temp, int n_left, int n_right) + { + int n_mid = (n_left + n_right) >> 1; + if (n_left >= n_right) { + return; + } + var range_l = new MergeSortRange() { Left = n_left, Right = n_mid }; + var range_r = new MergeSortRange() { Left = n_mid + 1, Right = n_right }; + ME.MergeSortSplit(json, json_path, callback, arr_json_temp, n_left, n_mid); + ME.MergeSortSplit(json, json_path, callback, arr_json_temp, n_mid + 1, n_right); + ME.MergeSortMerge(json, json_path, callback, arr_json_temp, range_l, range_r); + } + + private static void MergeSortMerge(STJson json, STJsonPath json_path, STJsonSortCallback callback, STJson[] arr_json_temp, MergeSortRange range_l, MergeSortRange range_r) + { + int n_index = 0; + int n_l_index = range_l.Left; + int n_r_index = range_r.Left; + if (json_path != null) { + while (n_l_index <= range_l.Right && n_r_index <= range_r.Right) { + var json_a = json_path.SelectFirst(json[n_l_index]); + var json_b = json_path.SelectFirst(json[n_r_index]); + if (callback(json_a, json_b) > 0) { + arr_json_temp[n_index++] = json[n_r_index++]; + } else { + arr_json_temp[n_index++] = json[n_l_index++]; + } + } + } else { + while (n_l_index <= range_l.Right && n_r_index <= range_r.Right) { + var json_a = json[n_l_index]; + var json_b = json[n_r_index]; + if (callback(json_a, json_b) > 0) { + arr_json_temp[n_index++] = json[n_r_index++]; + } else { + arr_json_temp[n_index++] = json[n_l_index++]; + } + } + } + while (n_l_index <= range_l.Right) { + arr_json_temp[n_index++] = json[n_l_index++]; + } + while (n_r_index <= range_r.Right) { + arr_json_temp[n_index++] = json[n_r_index++]; + } + for (int i = range_l.Left, j = 0; i <= range_r.Right; i++, j++) { + json[i] = arr_json_temp[j]; + } + } + + /* + private static void MergeSort(STJson json, int[] arr_index_src, int[] arr_index_temp, int n_left, int n_right) + { + int n_mid = (n_left + n_right) >> 1; + if (n_left >= n_right) { + return; + } + ME.MergeSort(json,arr_index_src, arr_index_temp, n_left, n_mid); + ME.MergeSort(json,arr_index_src, arr_index_temp, n_mid + 1, n_right); + } + + private static void MergeSortMerge(STJson json,System.Collections.IComparer aa) + { + + } + + public static STJson Sort(this STJson json) + { return json.Sort(false); } - public static STJson Sort(this STJson json, bool isDesc) { + public static STJson Sort(this STJson json, bool isDesc) + { return json.Sort(STJson.FromObject(new object[]{new { path = "", desc = isDesc } })); } - public static STJson Sort(this STJson json, params object[] fields) { + public static STJson Sort(this STJson json, params object[] fields) + { var jsonSort = STJson.CreateArray(); for (int i = 0; i < fields.Length; i += 2) { var j = new STJson() @@ -571,7 +637,8 @@ public static STJson Sort(this STJson json, params object[] fields) { return json.Sort(jsonSort); } - private static STJson Sort(this STJson json, STJson jsonSort) { + private static STJson Sort(this STJson json, STJson jsonSort) + { if (json.ValueType != STJsonValueType.Array) { throw new Exception(); } @@ -588,7 +655,8 @@ private static STJson Sort(this STJson json, STJson jsonSort) { return json_ret; } - private static MergeSortInfo[] GetMergeSortInfos(STJson json, STJson jsonSort) { + private static MergeSortInfo[] GetMergeSortInfos(STJson json, STJson jsonSort) + { double d_temp = 0; var arr_path = new STJsonPath[jsonSort.Count]; bool[] arr_b_desc = new bool[jsonSort.Count]; @@ -639,7 +707,8 @@ private static MergeSortInfo[] GetMergeSortInfos(STJson json, STJson jsonSort) { return msis; } - private static void MergeSort(MergeSortInfo[] msis_temp, MergeSortInfo[] msis_src, int nKeysLen, int nLeft, int nRight) { + private static void MergeSort(MergeSortInfo[] msis_temp, MergeSortInfo[] msis_src, int nKeysLen, int nLeft, int nRight) + { int nMid = (nLeft + nRight) / 2; if (nLeft >= nRight) { return; @@ -651,7 +720,8 @@ private static void MergeSort(MergeSortInfo[] msis_temp, MergeSortInfo[] msis_sr ME.MergeSortMerge(msis_temp, msis_src, nKeysLen, rangeL, rangeR); } - private static void MergeSortMerge(MergeSortInfo[] msis_temp, MergeSortInfo[] msis_src, int nKeysLen, MergeSortRange rangeL, MergeSortRange rangeR) { + private static void MergeSortMerge(MergeSortInfo[] msis_temp, MergeSortInfo[] msis_src, int nKeysLen, MergeSortRange rangeL, MergeSortRange rangeR) + { int nIndex = 0; int nLeft = rangeL.Left; int nRight = rangeR.Left; @@ -681,10 +751,11 @@ private static void MergeSortMerge(MergeSortInfo[] msis_temp, MergeSortInfo[] ms msis_src[i] = msis_temp[j]; } } - + */ // ================================================================================================== - public static STJson Min(this STJson json) { + public static STJson Min(this STJson json) + { int nCounter = 0; double d_min = double.MaxValue, d_val = 0; STJson json_min = STJson.CreateArray(); @@ -711,34 +782,36 @@ public static STJson Min(this STJson json) { .SetItem("items", json_min); } - public static STJson Min(this STJson json, params string[] strPaths) { - return json.Min(ME.StrToPath(strPaths)); + public static STJson Min(this STJson json, params string[] arr_str_path) + { + return json.Min(ME.StrToPath(arr_str_path)); } - public static STJson Min(this STJson json, params STJsonPath[] paths) { - ME.CheckPathsName(paths); + public static STJson Min(this STJson json, params STJsonPath[] arr_json_path) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + ME.CheckPathsName(arr_json_path); List lst_counter = new List(); List lst_min_val = new List(); List lst_min_json = new List(); - foreach (var v in paths) { + foreach (var v in arr_json_path) { lst_counter.Add(0); lst_min_val.Add(double.MaxValue); lst_min_json.Add(STJson.CreateArray()); } double d_val = 0; foreach (var v in json) { - for (int i = 0; i < paths.Length; i++) { + for (int i = 0; i < arr_json_path.Length; i++) { if (v.ValueType != STJsonValueType.Object) { continue; } - var json_item = paths[i].SelectFirst(v);// v[paths[i]]; + var json_item = arr_json_path[i].SelectFirst(v);// v[paths[i]]; if (json_item == null) { continue; } - if (json_item.ValueType != STJsonValueType.Long) { - continue; - } - if (json_item.ValueType != STJsonValueType.Double) { + if (json_item.ValueType != STJsonValueType.Long && json_item.ValueType != STJsonValueType.Double) { continue; } lst_counter[i]++; @@ -753,8 +826,8 @@ public static STJson Min(this STJson json, params STJsonPath[] paths) { } } STJson json_ret = new STJson(); - for (int i = 0; i < paths.Length; i++) { - json_ret.SetItem(paths[i].Name, new STJson() + for (int i = 0; i < arr_json_path.Length; i++) { + json_ret.SetItem(arr_json_path[i].Name, new STJson() .SetItem("count", lst_counter[i]) .SetItem("value", lst_counter[i] == 0 ? 0 : lst_min_val[i]) .SetItem("items", lst_min_json[i]) @@ -765,7 +838,8 @@ public static STJson Min(this STJson json, params STJsonPath[] paths) { // ================================================================================================== - public static STJson Max(this STJson json) { + public static STJson Max(this STJson json) + { int nCounter = 0; double d_min = double.MinValue, d_val = 0; STJson json_max = STJson.CreateArray(); @@ -792,34 +866,36 @@ public static STJson Max(this STJson json) { .SetItem("items", json_max); } - public static STJson Max(this STJson json, params string[] strPaths) { - return json.Max(ME.StrToPath(strPaths)); + public static STJson Max(this STJson json, params string[] arr_str_path) + { + return json.Max(ME.StrToPath(arr_str_path)); } - public static STJson Max(this STJson json, params STJsonPath[] paths) { - ME.CheckPathsName(paths); + public static STJson Max(this STJson json, params STJsonPath[] arr_json_path) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + ME.CheckPathsName(arr_json_path); List lst_counter = new List(); List lst_max_val = new List(); List lst_max_json = new List(); - foreach (var v in paths) { + foreach (var v in arr_json_path) { lst_counter.Add(0); lst_max_val.Add(double.MinValue); lst_max_json.Add(STJson.CreateArray()); } double d_val = 0; foreach (var v in json) { - for (int i = 0; i < paths.Length; i++) { + for (int i = 0; i < arr_json_path.Length; i++) { if (v.ValueType != STJsonValueType.Object) { continue; } - var json_item = paths[i].SelectFirst(v);// v[paths[i]]; + var json_item = arr_json_path[i].SelectFirst(v);// v[paths[i]]; if (json_item == null) { continue; } - if (json_item.ValueType != STJsonValueType.Long) { - continue; - } - if (json_item.ValueType != STJsonValueType.Double) { + if (json_item.ValueType != STJsonValueType.Long && json_item.ValueType != STJsonValueType.Double) { continue; } lst_counter[i]++; @@ -834,8 +910,8 @@ public static STJson Max(this STJson json, params STJsonPath[] paths) { } } STJson json_ret = new STJson(); - for (int i = 0; i < paths.Length; i++) { - json_ret.SetItem(paths[i].Name, new STJson() + for (int i = 0; i < arr_json_path.Length; i++) { + json_ret.SetItem(arr_json_path[i].Name, new STJson() .SetItem("count", lst_counter[i]) .SetItem("value", lst_counter[i] == 0 ? 0 : lst_max_val[i]) .SetItem("items", lst_max_json[i]) @@ -846,7 +922,8 @@ public static STJson Max(this STJson json, params STJsonPath[] paths) { // ================================================================================================== - public static STJson Sum(this STJson json) { + public static STJson Sum(this STJson json) + { int nCounter = 0; double d_sum = 0; foreach (var v in json) { @@ -862,24 +939,29 @@ public static STJson Sum(this STJson json) { return new STJson().SetItem("count", nCounter).SetItem("value", nCounter == 0 ? 0 : d_sum); } - public static STJson Sum(this STJson json, params string[] strPaths) { - return json.Sum(ME.StrToPath(strPaths)); + public static STJson Sum(this STJson json, params string[] arr_str_path) + { + return json.Sum(ME.StrToPath(arr_str_path)); } - public static STJson Sum(this STJson json, params STJsonPath[] paths) { - ME.CheckPathsName(paths); + public static STJson Sum(this STJson json, params STJsonPath[] arr_json_path) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + ME.CheckPathsName(arr_json_path); List lst_counter = new List(); List lst_sum = new List(); - foreach (var v in paths) { + foreach (var v in arr_json_path) { lst_counter.Add(0); lst_sum.Add(0); } foreach (var v in json) { - for (int i = 0; i < paths.Length; i++) { + for (int i = 0; i < arr_json_path.Length; i++) { if (v.ValueType != STJsonValueType.Object) { continue; } - var json_item = paths[i].SelectFirst(v);// v[paths[i]]; + var json_item = arr_json_path[i].SelectFirst(v);// v[paths[i]]; if (json_item == null) { continue; } @@ -891,8 +973,8 @@ public static STJson Sum(this STJson json, params STJsonPath[] paths) { } } STJson json_ret = new STJson(); - for (int i = 0; i < paths.Length; i++) { - json_ret.SetItem(paths[i].Name, new STJson() + for (int i = 0; i < arr_json_path.Length; i++) { + json_ret.SetItem(arr_json_path[i].Name, new STJson() .SetItem("count", lst_counter[i]) .SetItem("value", lst_counter[i] == 0 ? 0 : lst_sum[i]) ); @@ -902,7 +984,8 @@ public static STJson Sum(this STJson json, params STJsonPath[] paths) { // ================================================================================================== - public static STJson Avg(this STJson json) { + public static STJson Avg(this STJson json) + { int nCounter = 0; double d_sum = 0; foreach (var v in json) { @@ -918,31 +1001,33 @@ public static STJson Avg(this STJson json) { return new STJson().SetItem("count", nCounter).SetItem("value", nCounter == 0 ? 0 : d_sum / nCounter); } - public static STJson Avg(this STJson json, params string[] strPaths) { - return json.Avg(ME.StrToPath(strPaths)); + public static STJson Avg(this STJson json, params string[] arr_str_path) + { + return json.Avg(ME.StrToPath(arr_str_path)); } - public static STJson Avg(this STJson json, params STJsonPath[] paths) { - ME.CheckPathsName(paths); + public static STJson Avg(this STJson json, params STJsonPath[] arr_json_path) + { + if (json.ValueType != STJsonValueType.Array) { + throw new STJsonAggregateException("Current STJson is not a Array."); + } + ME.CheckPathsName(arr_json_path); List lst_counter = new List(); List lst_sum = new List(); - foreach (var v in paths) { + foreach (var v in arr_json_path) { lst_counter.Add(0); lst_sum.Add(0); } foreach (var v in json) { - for (int i = 0; i < paths.Length; i++) { + for (int i = 0; i < arr_json_path.Length; i++) { if (v.ValueType != STJsonValueType.Object) { continue; } - var json_item = paths[i].SelectFirst(v);// v[paths[i]]; + var json_item = arr_json_path[i].SelectFirst(v);// v[paths[i]]; if (json_item == null) { continue; } - if (json_item.ValueType != STJsonValueType.Long) { - continue; - } - if (json_item.ValueType != STJsonValueType.Double) { + if (json_item.ValueType != STJsonValueType.Long && json_item.ValueType != STJsonValueType.Double) { continue; } lst_counter[i]++; @@ -950,8 +1035,8 @@ public static STJson Avg(this STJson json, params STJsonPath[] paths) { } } STJson json_ret = new STJson(); - for (int i = 0; i < paths.Length; i++) { - json_ret.SetItem(paths[i].Name, new STJson() + for (int i = 0; i < arr_json_path.Length; i++) { + json_ret.SetItem(arr_json_path[i].Name, new STJson() .SetItem("count", lst_counter[i]) .SetItem("value", lst_counter[i] == 0 ? 0 : lst_sum[i] / lst_counter[i]) ); @@ -959,17 +1044,19 @@ public static STJson Avg(this STJson json, params STJsonPath[] paths) { return json_ret; } - private static STJsonPath[] StrToPath(string[] strPaths) { - var paths = new STJsonPath[strPaths.Length]; - for (int i = 0; i < strPaths.Length; i++) { - paths[i] = new STJsonPath(strPaths[i], strPaths[i]); + private static STJsonPath[] StrToPath(string[] arr_str_path) + { + var paths = new STJsonPath[arr_str_path.Length]; + for (int i = 0; i < arr_str_path.Length; i++) { + paths[i] = new STJsonPath(arr_str_path[i], arr_str_path[i]); } return paths; } - private static void CheckPathsName(STJsonPath[] paths) { + private static void CheckPathsName(STJsonPath[] arr_json_path) + { var hs_name = new HashSet(); - foreach (var v in paths) { + foreach (var v in arr_json_path) { if (!hs_name.Add(v.Name)) { throw new STJsonAggregateException("The same path name {" + v.Name + "}"); } diff --git a/Src/STLib.Json/JsonParser/STJson.DataType.cs b/Src/STLib.Json/JsonParser/STJson.DataType.cs deleted file mode 100644 index 17a4baf..0000000 --- a/Src/STLib.Json/JsonParser/STJson.DataType.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -namespace STLib.Json -{ - public enum STJsonSerilizaMode - { - All, Include, Exclude - } - - public enum STJsonValueType - { - Undefined, Long, Double, String, Boolean, Array, Object, Datetime - } - - public struct TypeMapInfo : IComparable - { - public int HashCode; - public Type Type; - public STJsonConverter Converter; - - public static TypeMapInfo Empty; - - public int CompareTo(object obj) { - return this.HashCode - ((TypeMapInfo)obj).HashCode; - } - - public static TypeMapInfo Create(Type t, STJsonConverter converter) { - return new TypeMapInfo() { - HashCode = t.GetHashCode(), - Type = t, - Converter = converter - }; - } - - public static TypeMapInfo Create(int nCode, STJsonConverter converter) { - return new TypeMapInfo() { - HashCode = nCode, - Converter = converter - }; - } - - public override string ToString() { - return this.Type.FullName; - } - } -} - diff --git a/Src/STLib.Json/JsonParser/STJsonTokenizer.cs b/Src/STLib.Json/JsonParser/STJsonTokenizer.cs deleted file mode 100644 index 2c96dbd..0000000 --- a/Src/STLib.Json/JsonParser/STJsonTokenizer.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Text; - -using ME = STLib.Json.STJsonTokenizer; - -namespace STLib.Json -{ - internal class STJsonTokenizer - { - public enum TokenType - { - KeyWord, Long, Double, String, ItemSplitor, KVSplitor, ObjectStart, ObjectEnd, ArrayStart, ArrayEnd - } - - public struct Token - { - public int Index; - public string Value; - public TokenType Type; - - public Token(int nIndex, string strValue, TokenType type) { - this.Index = nIndex; - this.Value = strValue; - this.Type = type; - } - - public override string ToString() { - return string.Format("\"{0}\" - [{1}] - [{2}]", this.Value, this.Index, this.Type); - } - } - - private static Dictionary m_dic_char_hex = new Dictionary(); - - static STJsonTokenizer() { - for (int i = 0; i <= 0xFFFF; i++) { - if (i <= 0xFF) { - m_dic_char_hex.Add(i.ToString("X").PadLeft(2, '0'), ((char)i).ToString()); - } - m_dic_char_hex.Add(i.ToString("X").PadLeft(4, '0'), ((char)i).ToString()); - } - } - - public static List GetTokens(string strJson) { - Token token = new Token(); - List lst = new List(); - Stack stack_region = new Stack(); - for (int i = 0; i < strJson.Length; i++) { - var c = strJson[i]; - if (('0' <= c && c <= '9') || c == '-') { - token = ME.GetNumber(strJson, i); - lst.Add(token); - i += token.Value.Length - 1; - continue; - } - switch (c) { - case '{': // object start - stack_region.Push('{'); - lst.Add(new Token(i, "{", TokenType.ObjectStart)); - continue; - case '[': // array start - stack_region.Push('['); - lst.Add(new Token(i, "[", TokenType.ArrayStart)); - continue; - case ',': // item splitor - lst.Add(new Token(i, ",", TokenType.ItemSplitor)); - continue; - case ':': // key value splitor - lst.Add(new Token(i, ":", TokenType.KVSplitor)); - continue; - case ']': // array end - if (stack_region.Count == 0 || stack_region.Pop() != '[') { - throw new STJsonParseException(i, "Invalid char, missing '[' or '}'. Index: " + i); - } - lst.Add(new Token(i, "]", TokenType.ArrayEnd)); - continue; - case '}': // object end - if (stack_region.Count == 0 || stack_region.Pop() != '{') { - throw new STJsonParseException(i, "Invalid char, missing '[' or '}'. Index: " + i); - } - lst.Add(new Token(i, "}", TokenType.ObjectEnd)); - continue; - case 't': // [keyword] - true - lst.Add(ME.GetKeyWord(strJson, i, "true")); - i += 3; - continue; - case 'f': // [keyword] - false - lst.Add(ME.GetKeyWord(strJson, i, "false")); - i += 4; - continue; - case 'n': // [keyword] - null - lst.Add(ME.GetKeyWord(strJson, i, "null")); - i += 3; - continue; - case '"': // string - token = ME.GetString(strJson, i); - i += token.Value.Length + 1; - token.Value = ME.ParseString(token); - lst.Add(token); - continue; - case ' ': - case '\r': // space - case '\n': - case '\t': - continue; - default: - throw new STJsonParseException(i, "Invalid char. Index: " + i); - } - } - return lst; - } - - private static Token GetNumber(string strText, int nIndex) { - bool bDot = false, bE = false; - Token token = new Token() { - Index = nIndex, - Type = TokenType.Long - }; - for (int i = nIndex; i < strText.Length; i++) { - var c = strText[i]; - if (c == '-') { // -123 - if (i != nIndex) { - throw new STJsonParseException(i, "Invalid char. Index: " + i); - } - continue; - } - if (c == '.') { // 1.23 - if (bDot || bE || i == nIndex || strText[i - 1] == '-') { - throw new STJsonParseException(i, "Invalid char. Index: " + i); - } - bDot = true; - token.Type = TokenType.Double; - continue; - } - if (c == 'E' || c == 'e') { // 12E+3 Regex:-?\d+(\.\d+)?E[+-]?(\d+) - if (bE || i == nIndex || strText[i - 1] == '-' || strText[i - 1] == '.') { - throw new STJsonParseException(i, "Invalid char. Index: " + i); - } - if (i + 2 >= strText.Length) { - throw new STJsonParseException(i, "Invalid char. Index: " + i); - } - c = strText[++i]; - if (c == '-' || c == '+') { // E+ | E- - c = strText[++i]; - } - //if (strText[++i] != '+') { // E+ - // throw new STJsonParseException(i + 1, "Invalid char. Index: " + (i + 1)); - //} - bE = true; - //c = strText[++i]; - if (c < '0' && '9' < c) { // E+[0-9] - throw new STJsonParseException(i + 2, "Invalid char. Index: " + (i + 2)); - } - token.Type = TokenType.Double; - continue; - } - if ('0' <= c && c <= '9') continue; - token.Value = strText.Substring(nIndex, i - nIndex); - return token; - } - token.Value = strText.Substring(nIndex); - return token; - } - - private static Token GetKeyWord(string strText, int nIndex, string strKeyWord) { - Token token = new Token() { - Index = nIndex, - Type = TokenType.KeyWord - }; - if (strText.Substring(nIndex, strKeyWord.Length) == strKeyWord) { - token.Value = strKeyWord; - return token; - } - throw new STJsonParseException(nIndex, "Invalid char. Index: " + nIndex); - } - - private static Token GetString(string strText, int nIndex) { - //char ch_last = '\0'; - Token token = new Token() { - Index = nIndex, - Type = TokenType.String - }; - if (strText[nIndex] != '"') { - return token; - } - for (int i = nIndex + 1; i < strText.Length; i++) { - var ch = strText[i]; - if (ch == '\\') { - i++; - continue; - } - if (ch == '"') { - token.Value = strText.Substring(nIndex + 1, i - nIndex - 1); - return token; - } - //if (ch_last == '\\') ch_last = '\0'; - //if (ch == '"' && ch_last != '\\') { - // token.Value = strText.Substring(nIndex + 1, i - nIndex - 1); - // return token; - //} - //ch_last = ch; - } - throw new STJsonParseException(nIndex, "Can not get a string. Index: " + nIndex); - } - - private static string ParseString(Token token) { - int nHexLen = 0; - string strTemp = string.Empty; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < token.Value.Length; i++) { - var ch = token.Value[i]; - if (ch != '\\') { - sb.Append(ch); - continue; - } - i++; - if (i >= token.Value.Length) { - throw new STJsonParseException(token.Index + i, ch); - } - ch = token.Value[i]; - switch (ch) { - case 'r': sb.Append('\r'); continue; - case 'n': sb.Append('\n'); continue; - case 't': sb.Append('\t'); continue; - case 'f': sb.Append('\f'); continue; - case 'b': sb.Append('\b'); continue; - case 'a': sb.Append('\a'); continue; - case 'v': sb.Append('\v'); continue; - case '0': sb.Append('\0'); continue; - case 'x': - case 'u': - nHexLen = ch == 'x' ? 2 : 4; - if (i + nHexLen >= token.Value.Length) { - throw new STJsonParseException(token.Index + i, ch); - } - strTemp = token.Value.Substring(i + 1, nHexLen).ToUpper(); - if (!m_dic_char_hex.ContainsKey(strTemp)) { - throw new STJsonParseException(token.Index + i + 1, strTemp[0]); - } - sb.Append(m_dic_char_hex[strTemp]); - i += nHexLen; - continue; - default: - sb.Append(ch); - continue; - } - } - return sb.ToString(); - } - } -} - diff --git a/Src/STLib.Json/JsonPathParser/STJsonPathBuildInFunctions.cs b/Src/STLib.Json/JsonPathParser/STJsonPathBuildInFunctions.cs deleted file mode 100644 index 896e268..0000000 --- a/Src/STLib.Json/JsonPathParser/STJsonPathBuildInFunctions.cs +++ /dev/null @@ -1,424 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -using ME = STLib.Json.STJsonPathBuildInFunctions; - -namespace STLib.Json -{ - internal class STJsonPathBuildInFunctions - { - private delegate ExpressionToken GeneralCallBack(ExpressionToken token); - - private static List m_lst_info; - - public static STJson FuncList { get; private set; } - - [STJson(STJsonSerilizaMode.Exclude)] - private struct FNInfo - { - public string name; - public string[] demos; - [STJsonProperty] - public STJsonPathBuildInFuncHander func; - - public FNInfo(string strName, STJsonPathBuildInFuncHander fn, string[] demos) { - this.name = strName; - this.func = fn; - this.demos = demos; - } - } - - public static void Init() { - m_lst_info = new List(); - m_lst_info.Add(new FNInfo("typeof", ME.FN_typeof, "(object) -> typeof('abc')|(array,bool) -> typeof(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("str", ME.FN_str, "(object) -> str('abc')|(array,bool) -> str(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("upper", ME.FN_upper, "(object) -> upper('abc')|(array,bool) -> upper(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("lower", ME.FN_lower, "(object) -> lower('abc')|(array,bool) -> lower(['abc',123],true)".Split('|'))); - - m_lst_info.Add(new FNInfo("len", ME.FN_len, "(object) -> len('abc')|(array,bool) -> len(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("long", ME.FN_long, "(object) -> long('abc')|(array,bool) -> long(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("double", ME.FN_double, "(object) -> double('abc')|(array,bool) -> double(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("bool", ME.FN_bool, "(object) -> bool('abc')|(array,bool) -> bool(['abc',123],true)".Split('|'))); - - m_lst_info.Add(new FNInfo("abs", ME.FN_abs, "(object) -> abs('abc')|(array,bool) -> abs(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("round", ME.FN_round, "(object) -> round('abc')|(array,bool) -> round(['abc',123],true)".Split('|'))); - m_lst_info.Add(new FNInfo("ceil", ME.FN_lower, "(object) -> ceil('abc')|(array,bool) -> ceil(['abc',123],true)".Split('|'))); - - m_lst_info.Add(new FNInfo("max", ME.FN_max, "(array) -> max([1,2,3,4,5])".Split('|'))); - m_lst_info.Add(new FNInfo("min", ME.FN_min, "(array) -> max([1,2,3,4,5])".Split('|'))); - m_lst_info.Add(new FNInfo("avg", ME.FN_avg, "(array) -> max([1,2,3,4,5])".Split('|'))); - m_lst_info.Add(new FNInfo("sum", ME.FN_sum, "(array) -> max([1,2,3,4,5])".Split('|'))); - - m_lst_info.Add(new FNInfo("trim", ME.FN_trim, "(string) -> trim('abc')|(string,string) -> trim(',.abc.,','.,')".Split('|'))); - m_lst_info.Add(new FNInfo("trims", ME.FN_trims, "(string) -> trims('abc')|(string,string) -> trims(',.abc.,','.,')".Split('|'))); - m_lst_info.Add(new FNInfo("trime", ME.FN_trime, "(string) -> trime('abc')|(string,string) -> trime(',.abc.,','.,')".Split('|'))); - m_lst_info.Add(new FNInfo("split", ME.FN_split, @"(string,regex_string) -> split('a.b.c','\\.')".Split('|'))); - - m_lst_info.Add(new FNInfo("time", ME.FN_time, "() -> time()|(string) -> time('yyyy-MM-dd')|(long,string) -> time(1684751411726,'yyyy-MM-dd')".Split('|'))); - - foreach (var v in m_lst_info) { - STJsonPath.BuildInFunctions.Add(v.name, v.func); - } - - ME.FuncList = STJson.FromObject(m_lst_info, STJsonSetting.Default); - } - - public static ExpressionToken FN_typeof(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_typeof, false); - } - - private static ExpressionToken FN_typeof(ExpressionToken token) { - switch (token.Type) { - case ExpressTokenType.String: return ExpressionToken.Create(-1, ExpressTokenType.String, "string"); - case ExpressTokenType.Long: return ExpressionToken.Create(-1, ExpressTokenType.String, "long"); - case ExpressTokenType.Double: return ExpressionToken.Create(-1, ExpressTokenType.String, "double"); - case ExpressTokenType.Boolean: return ExpressionToken.Create(-1, ExpressTokenType.String, "boolean"); - case ExpressTokenType.Array: return ExpressionToken.Create(-1, ExpressTokenType.String, "array"); - case ExpressTokenType.Object: return ExpressionToken.Create(-1, ExpressTokenType.String, "object"); - } - return ExpressionToken.Undefined; - } - - public static ExpressionToken FN_str(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_str, false); - } - - private static ExpressionToken FN_str(ExpressionToken token) { - return ExpressionToken.Create(-1, ExpressTokenType.String, Convert.ToString(token.Value)); - } - - public static ExpressionToken FN_upper(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_upper, true); - } - - private static ExpressionToken FN_upper(ExpressionToken token) { - switch (token.Type) { - case ExpressTokenType.String: - return ExpressionToken.Create(-1, ExpressTokenType.String, Convert.ToString(token.Value).ToUpper()); - } - return token; - } - - public static ExpressionToken FN_lower(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_lower, true); - } - - private static ExpressionToken FN_lower(ExpressionToken token) { - switch (token.Type) { - case ExpressTokenType.String: - return ExpressionToken.Create(-1, ExpressTokenType.String, Convert.ToString(token.Value).ToLower()); - } - return token; - } - - public static ExpressionToken FN_len(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_len, false); - } - - private static ExpressionToken FN_len(ExpressionToken token) { - switch (token.Type) { - case ExpressTokenType.String: - return ExpressionToken.Create(-1, ExpressTokenType.Long, Convert.ToString(token.Value).Length); - case ExpressTokenType.Array: - return ExpressionToken.Create(-1, ExpressTokenType.Long, (token.Value as ExpressionToken[]).Length); - } - return ExpressionToken.Undefined; - } - - public static ExpressionToken FN_long(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_long, true); - } - - private static ExpressionToken FN_long(ExpressionToken token) { - try { - return ExpressionToken.Create(-1, ExpressTokenType.Long, Convert.ToInt64(token.Value)); - } catch { - return ExpressionToken.Undefined; - } - } - - public static ExpressionToken FN_double(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_double, true); - } - - private static ExpressionToken FN_double(ExpressionToken token) { - try { - return ExpressionToken.Create(-1, ExpressTokenType.Long, Convert.ToDouble(token.Value)); - } catch { - return ExpressionToken.Undefined; - } - } - - public static ExpressionToken FN_bool(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_bool, true); - } - - private static ExpressionToken FN_bool(ExpressionToken token) { - try { - return ExpressionToken.Create(-1, ExpressTokenType.Boolean, Convert.ToBoolean(token.Value)); - } catch { - return ExpressionToken.Create(-1, ExpressTokenType.Boolean, false); - } - } - - public static ExpressionToken FN_abs(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_abs, true); - } - - private static ExpressionToken FN_abs(ExpressionToken token) { - long l_v = 0; - double d_v = 0; - switch (token.Type) { - case ExpressTokenType.Long: - l_v = Convert.ToInt64(token.Value); - if (l_v < 0) { - token.Value = -l_v; - } - break; - case ExpressTokenType.Double: - d_v = Convert.ToDouble(token.Value); - if (d_v < 0) { - token.Value = -d_v; - } - break; - } - return token; - } - - public static ExpressionToken FN_round(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_round, true); - } - - private static ExpressionToken FN_round(ExpressionToken token) { - switch (token.Type) { - case ExpressTokenType.Double: - token.Type = ExpressTokenType.Long; - token.Value = (long)Math.Round(Convert.ToDouble(token.Value)); - break; - } - return token; - } - - public static ExpressionToken FN_ceil(ExpressionToken[] args) { - return ME.FNGeneral(args, ME.FN_ceil, true); - } - - private static ExpressionToken FN_ceil(ExpressionToken token) { - switch (token.Type) { - case ExpressTokenType.Double: - token.Type = ExpressTokenType.Long; - token.Value = (long)Math.Ceiling(Convert.ToDouble(token.Value)); - break; - } - return token; - } - - - private static ExpressionToken FN_max(ExpressionToken[] args) { - if (args == null || args.Length != 1) { - return ExpressionToken.Undefined; - } - if (args[0].Type != ExpressTokenType.Array) { - return ExpressionToken.Undefined; - } - ExpressionToken token = ExpressionToken.Create(-1, ExpressTokenType.Double, double.MinValue); - double d_v = double.MinValue, d_t = 0; - foreach (var v in args[0].Value as ExpressionToken[]) { - if (!v.IsNumber) { - continue; - } - d_t = Convert.ToDouble(v.Value); - if (d_t > d_v) { - token = v; - d_v = d_t; - } - } - return token; - } - - private static ExpressionToken FN_min(ExpressionToken[] args) { - if (args == null || args.Length != 1) { - return ExpressionToken.Undefined; - } - if (args[0].Type != ExpressTokenType.Array) { - return ExpressionToken.Undefined; - } - ExpressionToken token = ExpressionToken.Create(-1, ExpressTokenType.Double, double.MaxValue); - double d_v = double.MaxValue, d_t = 0; - foreach (var v in args[0].Value as ExpressionToken[]) { - if (!v.IsNumber) { - continue; - } - d_t = Convert.ToDouble(v.Value); - if (d_t < d_v) { - token = v; - d_v = d_t; - } - } - return token; - } - - private static ExpressionToken FN_avg(ExpressionToken[] args) { - if (args == null || args.Length != 1) { - return ExpressionToken.Undefined; - } - if (args[0].Type != ExpressTokenType.Array) { - return ExpressionToken.Undefined; - } - double d_v = 0; - int nCounter = 0; - foreach (var v in args[0].Value as ExpressionToken[]) { - if (!v.IsNumber) { - continue; - } - nCounter++; - d_v += Convert.ToDouble(v.Value); - } - if (nCounter == 0) { - return ExpressionToken.Create(-1, ExpressTokenType.Double, 0); - } - return ExpressionToken.Create(-1, ExpressTokenType.Double, d_v / nCounter); - } - - private static ExpressionToken FN_sum(ExpressionToken[] args) { - if (args == null || args.Length != 1) { - return ExpressionToken.Undefined; - } - if (args[0].Type != ExpressTokenType.Array) { - return ExpressionToken.Undefined; - } - double d_v = 0; - foreach (var v in args[0].Value as ExpressionToken[]) { - if (!v.IsNumber) { - continue; - } - d_v += Convert.ToDouble(v.Value); - } - return ExpressionToken.Create(-1, ExpressTokenType.Double, d_v); - } - - - public static ExpressionToken FN_split(ExpressionToken[] args) { - if (args == null || args.Length != 2) { - return ExpressionToken.Undefined; - } - if (args[0].Type != ExpressTokenType.String || args[1].Type != ExpressTokenType.String) { - return ExpressionToken.Undefined; - } - try { - Regex reg = new Regex(args[1].Value.ToString()); - var strs = reg.Split(args[0].Value.ToString()); - var results = new ExpressionToken[strs.Length]; - for (int i = 0; i < strs.Length; i++) { - results[i] = ExpressionToken.Create(-1, ExpressTokenType.String, strs[i]); - } - return ExpressionToken.Create(-1, ExpressTokenType.Array, results); - } catch { - return ExpressionToken.Undefined; - } - } - - public static ExpressionToken FN_trim(ExpressionToken[] args) { - return ME.FN_trim(args, 0); - } - - private static ExpressionToken FN_trims(ExpressionToken[] args) { - return ME.FN_trim(args, 1); - } - - private static ExpressionToken FN_trime(ExpressionToken[] args) { - return ME.FN_trim(args, 2); - } - - private static ExpressionToken FN_trim(ExpressionToken[] args, int nModel) { - if (args == null || args.Length < 1) { - return ExpressionToken.Undefined; - } - if (args[0].Type != ExpressTokenType.String) { - return ExpressionToken.Undefined; - } - char[] chTrim = null; - try { - if (args.Length > 1 && args[1].Type == ExpressTokenType.String) { - var strText = args[1].Value.ToString(); - chTrim = new char[strText.Length]; - for (int i = 0; i < strText.Length; i++) { - chTrim[i] = strText[i]; - } - } else { - chTrim = new char[] { ' ', '\t', '\r', '\n' }; - } - switch (nModel) { - case 0: - args[0].Value = args[0].Value.ToString().Trim(chTrim); - break; - case 1: - args[0].Value = args[0].Value.ToString().TrimStart(chTrim); - break; - case 2: - args[0].Value = args[0].Value.ToString().TrimEnd(chTrim); - break; - } - return args[0]; - } catch { - return ExpressionToken.Undefined; - } - } - - public static ExpressionToken FN_time(ExpressionToken[] args) { - if (args == null || args.Length == 0) { - return ExpressionToken.Create(-1, ExpressTokenType.Long, (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds); - } - if (args.Length == 1) { - if (args[0].Type == ExpressTokenType.String) { - return ExpressionToken.Create(-1, ExpressTokenType.String, DateTime.Now.ToString(args[0].Value.ToString())); - } - } - if (args.Length == 2) { - if (args[0].Type == ExpressTokenType.Long && args[1].Type == ExpressTokenType.String) { - var strTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)) - .AddMilliseconds(Convert.ToInt64(args[0].Value)) - .ToString(args[1].Value.ToString()); - return ExpressionToken.Create(-1, ExpressTokenType.String, strTime); - } - } - return ExpressionToken.Undefined; - } - - - private static ExpressionToken FNGeneral(ExpressionToken[] args, GeneralCallBack callBack, bool bDepth) { - if (args == null || args.Length < 1) { - return ExpressionToken.Undefined; - } - if (args.Length > 1) { - bDepth = (bool)ME.FN_bool(args[1]).Value; - } - //bDepth |= args[0].Type == ExpressTokenType.Array; - if (bDepth) { - var arr = ME.GetArgs(args[0]); - return ME.FNGeneral(arr, callBack); - } - return callBack(args[0]); - } - - private static ExpressionToken FNGeneral(ExpressionToken[] tokens, GeneralCallBack callBack) { - ExpressionToken[] results = new ExpressionToken[tokens.Length]; - for (int i = 0; i < tokens.Length; i++) { - if (tokens[i].Type == ExpressTokenType.Array) { - results[i] = ME.FNGeneral(tokens[i].Value as ExpressionToken[], callBack); - } else { - results[i] = callBack(tokens[i]); - } - } - return ExpressionToken.Create(-1, ExpressTokenType.Array, results); - } - - private static ExpressionToken[] GetArgs(ExpressionToken token) { - if (token.Type == ExpressTokenType.Array) { - return token.Value as ExpressionToken[]; - } - return new ExpressionToken[] { token }; - } - } -} diff --git a/Src/STLib.Json/STJson.Static.cs b/Src/STLib.Json/STJson.Static.cs deleted file mode 100644 index 300212b..0000000 --- a/Src/STLib.Json/STJson.Static.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace STLib.Json -{ - public partial class STJson - { - internal static List m_lst_key_converter = new List(); - internal static List m_lst_type_converter = new List(); - - internal static Dictionary m_dic_key_converter = new Dictionary(); - internal static Dictionary m_dic_type_converter = new Dictionary(); - - public static void AddCustomConverter(Type t, STJsonConverter converter) { - int nCode = t.GetHashCode(); - if (m_dic_type_converter.ContainsKey(nCode)) { - m_dic_type_converter[nCode] = converter; - } else { - m_dic_type_converter.Add(nCode, converter); - } - } - - public static void AddCustomConverter(string strKey, STJsonConverter converter) { - int nCode = strKey.GetHashCode(); - if (m_dic_key_converter.ContainsKey(nCode)) { - m_dic_key_converter[nCode] = converter; - } else { - m_dic_key_converter.Add(nCode, converter); - } - } - - public static void RemoveCustomConverter(Type type) { - int nCode = type.GetHashCode(); - if (m_dic_type_converter.ContainsKey(nCode)) { - m_dic_type_converter.Remove(nCode); - } - } - - public static void RemoveCustomConverter(string strKey) { - int nCode = strKey.GetHashCode(); - if (m_dic_key_converter.ContainsKey(nCode)) { - m_dic_key_converter.Remove(nCode); - } - } - - public static STJsonConverter GetConverter(Type type) { - int nCode = type.GetHashCode(); - if (m_dic_type_converter.ContainsKey(nCode)) { - return m_dic_type_converter[nCode]; - } else { - return STJsonBuildInConverter.Get(nCode); - } - } - - public static STJsonConverter GetCustomConverter(Type type) { - int nCode = type.GetHashCode(); - if (m_dic_type_converter.ContainsKey(nCode)) { - return m_dic_type_converter[nCode]; - } - return null; - } - - public static STJsonConverter GetCustomConverter(string strKey) { - int nCode = strKey.GetHashCode(); - if (m_dic_key_converter.ContainsKey(nCode)) { - return m_dic_key_converter[nCode]; - } - return null; - } - - public static string Serialize(object obj) { - StringBuilder sb = new StringBuilder(512); - ObjectToString.Get(sb, obj, STJsonSetting.Default); - return sb.ToString(); - } - - public static string Serialize(object obj, int nSpaceCount) { - StringBuilder sb = new StringBuilder(512); - ObjectToString.Get(sb, obj, STJsonSetting.Default); - return STJson.Format(sb.ToString(), nSpaceCount); - } - - public static string Serialize(object obj, STJsonSetting setting) { - StringBuilder sb = new StringBuilder(512); - ObjectToString.Get(sb, obj, setting); - return sb.ToString(); - } - - public static string Serialize(object obj, int nSpaceCount, STJsonSetting setting) { - StringBuilder sb = new StringBuilder(512); - ObjectToString.Get(sb, obj, setting); - return STJson.Format(sb.ToString(), nSpaceCount); - } - - public static STJson Deserialize(string strJson) { - return StringToSTJson.Get(strJson); - } - - public static T Deserialize(string strJson) { - var json = StringToSTJson.Get(strJson); - return STJsonToObject.Get(json, STJsonSetting.Default); - } - - public static T Deserialize(STJson stJson) { - return STJsonToObject.Get(stJson, STJsonSetting.Default); - } - - public static void Deserialize(string strJson, object obj) { - var json = StringToSTJson.Get(strJson); - STJsonToObject.SetObject(json, obj, STJsonSetting.Default); - } - - public static void Deserialize(STJson stJson, object obj) { - STJsonToObject.SetObject(stJson, obj, STJsonSetting.Default); - } - - public static T Deserialize(string strJson, STJsonSetting setting) { - var json = StringToSTJson.Get(strJson); - return STJsonToObject.Get(json, setting); - } - - public static T Deserialize(STJson stJson, STJsonSetting setting) { - return STJsonToObject.Get(stJson, setting); - } - - public static void Deserialize(string strJson, object obj, STJsonSetting setting) { - var json = StringToSTJson.Get(strJson); - STJsonToObject.SetObject(json, obj, setting); - } - - public static void Deserialize(STJson stJson, object obj, STJsonSetting setting) { - STJsonToObject.SetObject(stJson, obj, setting); - } - - public static STJson New() { - return new STJson(); - } - - public static STJson CreateObject() { - STJson json = new STJson(); - json.SetModel(STJsonValueType.Object); - return json; - } - - public static STJson CreateArray(params object[] objs) { - STJson json = new STJson(); - json.SetModel(STJsonValueType.Array); - foreach (var v in objs) { - json.Append(v); - } - return json; - } - - public static STJson FromObject(string value) { - STJson json = new STJson(); - json.SetValue(value); - return json; - } - - public static STJson FromObject(int value) { - STJson json = new STJson(); - json.SetValue(value); - return json; - } - - public static STJson FromObject(long value) { - STJson json = new STJson(); - json.SetValue(value); - return json; - } - - public static STJson FromObject(float value) { - STJson json = new STJson(); - json.SetValue(value); - return json; - } - - public static STJson FromObject(double value) { - STJson json = new STJson(); - json.SetValue(value); - return json; - } - - public static STJson FromObject(bool value) { - STJson json = new STJson(); - json.SetValue(value); - return json; - } - - public static STJson FromObject(DateTime dateTime) { - STJson json = new STJson(); - json.SetValue(dateTime); - return json; - } - - public static STJson FromObject(object obj) { - return ObjectToSTJson.Get(obj, STJsonSetting.Default); - } - - public static STJson FromObject(object obj, STJsonSetting setting) { - if (obj is STJson) return obj as STJson; - return ObjectToSTJson.Get(obj, setting); - } - - public static string Format(string strJson) { - return STJson.Format(strJson, 4); - } - - public static string Format(string strJson, int nSpaceCount) { - char ch_last = '\0'; - StringBuilder sb = new StringBuilder(); - int nLevel = 0; - bool bString = false; - bool bArray = false; - Stack stack_is_array = new Stack(); - foreach (var c in strJson) { - if (c == '"' && ch_last != '\\') { - bString = !bString; - } - if (!bString) { - switch (c) { - case ':': - sb.Append(": "); - continue; - case ',': - if (bArray) { - sb.Append(", "); - } else { - sb.Append(c); - sb.Append("\r\n".PadRight(nLevel * nSpaceCount + 2)); // 2 -> \r\n - } - continue; - case '{': - case '[': - bArray = c == '['; - stack_is_array.Push(bArray); - nLevel++; - sb.Append(c); - sb.Append("\r\n".PadRight(nLevel * nSpaceCount + 2)); - continue; - case '}': - case ']': - if (stack_is_array.Count == 0) { - bArray = false; - } else { - stack_is_array.Pop(); - bArray = stack_is_array.Count == 0 ? false : stack_is_array.First(); - } - nLevel--; - sb.Append("\r\n".PadRight(nLevel * nSpaceCount + 2) + c); - continue; - } - } - sb.Append(c); - ch_last = c; - } - return sb.ToString(); - } - - internal static string Escape(string strText) { - if (string.IsNullOrEmpty(strText)) return strText; - StringBuilder sb = new StringBuilder(strText.Length); - for (int i = 0; i < strText.Length; i++) { - var ch = strText[i]; - if (0x5D <= ch && ch <= 0x10FFFF) { - sb.Append(ch); - continue; - } - if (0x23 <= ch && ch <= 0x5B) { - sb.Append(ch); - continue; - } - if (0x20 <= ch && ch <= 0x21) { - sb.Append(ch); - continue; - } - switch (strText[i]) { - case '\"': sb.Append("\\\""); continue; - case '\\': sb.Append("\\\\"); continue; - case '\b': sb.Append("\\b"); continue; - case '\f': sb.Append("\\f"); continue; - case '\n': sb.Append("\\n"); continue; - case '\r': sb.Append("\\r"); continue; - case '\t': sb.Append("\\t"); continue; - //case '\0': sb.Append("\\0"); continue; - //case '\a': sb.Append("\\a"); continue; - //case '\v': sb.Append("\\v"); continue; - //case '\x7F': sb.Append("\\u007F"); continue; - default: - //if (strText[i] < 32) { - // sb.Append("\\x" + ((int)strText[i]).ToString("X4")); - //} else { - // sb.Append(strText[i]); - //} - sb.Append("\\u" + ((int)ch).ToString("X4")); - continue; - } - } - if (sb.Length == strText.Length) { - return strText; - } - return sb.ToString(); - } - } -} - diff --git a/Src/STLib.Json/STJson.cs b/Src/STLib.Json/STJson.cs deleted file mode 100644 index 1fccdf8..0000000 --- a/Src/STLib.Json/STJson.cs +++ /dev/null @@ -1,463 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; - -namespace STLib.Json -{ - public partial class STJson : IEnumerable, ICloneable - { - public string Key { get; private set; } - public object Value { get; private set; } - public bool IsNullObject { - get { - if (this.Value == null) { - return m_dic_values == null && m_lst_values == null; - } - return false; - } - } - public bool IsNumber { - get { return this.ValueType == STJsonValueType.Long || this.ValueType == STJsonValueType.Double; } - } - public STJsonValueType ValueType { get; internal set; } - public int Count { - get { - switch (this.ValueType) { - case STJsonValueType.Array: - return m_lst_values.Count; - case STJsonValueType.Object: - return m_dic_values.Count; - } - return 0; - } - } - - public STJson this[string key] { - get { - if (this.ValueType != STJsonValueType.Object) { - throw new STJsonException("Current STJson is not Object."); - } - if (m_dic_values.ContainsKey(key)) { - return m_dic_values[key]; - } - return null; - } - } - - public STJson this[int index] { - get { - if (this.ValueType != STJsonValueType.Array) { - throw new STJsonException("Current STJson is not Array."); - } - return m_lst_values[index]; - } - } - - private List m_lst_values; - private Dictionary m_dic_values; - - public STJson() { } - - private STJson(string key) { - if (key == null) { - key = "null"; - } - this.Key = key; - } - - public string[] GetKeys() { - if (m_dic_values == null) { - return null; - } - int i = 0; - string[] strs = new string[m_dic_values.Count]; - foreach (var v in m_dic_values.Keys) { - strs[i++] = v; - } - return strs; - } - - public STJson SetKey(string key) { - this.SetModel(STJsonValueType.Object); - if (key == null) { - key = "null"; - } - if (!m_dic_values.ContainsKey(key)) { - m_dic_values.Add(key, new STJson(key)); - } - return m_dic_values[key]; - } - - public STJson SetItem(string key, string value) { - STJson json = this.SetKey(key); - json.SetValue(value); - return this; - } - - public STJson SetItem(string key, int value) { - STJson json = this.SetKey(key); - json.SetValue(value); - return this; - } - - public STJson SetItem(string key, long value) { - STJson json = this.SetKey(key); - json.SetValue(value); - return this; - } - - public STJson SetItem(string key, double value) { - STJson json = this.SetKey(key); - json.SetValue(value); - return this; - } - - public STJson SetItem(string key, bool value) { - STJson json = this.SetKey(key); - json.SetValue(value); - return this; - } - - public STJson SetItem(string key, DateTime value) { - STJson json = this.SetKey(key); - json.SetValue(value); - return this; - } - - public STJson SetItem(string key, STJson json) { - this.SetKey(key).SetValue(json); - return this; - } - - public STJson SetItem(string key, object obj) { - STJson json = this.SetKey(key); - json.SetValue(obj); - return this; - } - - - public STJson Delete(string key) { - if (this.ValueType != STJsonValueType.Object) { - throw new STJsonException("Current STJson is not Object."); - } - if (key == null) { - key = "null"; - } - STJson json = null; - if (m_dic_values.ContainsKey(key)) { - json = m_dic_values[key]; - m_dic_values.Remove(key); - } - return json; - } - - - public STJson Append(string value) { - return this.Append(STJson.FromObject(value)); - } - - public STJson Append(int value) { - return this.Append(STJson.FromObject(value)); - } - - public STJson Append(long value) { - return this.Append(STJson.FromObject(value)); - } - - public STJson Append(double value) { - return this.Append(STJson.FromObject(value)); - } - - public STJson Append(bool value) { - return this.Append(STJson.FromObject(value)); - } - - public STJson Append(DateTime dateTime) { - return this.Append(STJson.FromObject(dateTime)); - } - - public STJson Append(STJson json) { - this.SetModel(STJsonValueType.Array); - m_lst_values.Add(json); - return this; - } - - public STJson Append(params object[] objs) { - this.SetModel(STJsonValueType.Array); - foreach (var v in objs) { - this.Append(STJson.FromObject(v)); - } - return this; - } - - public STJson Insert(int nIndex, string value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, int value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, long value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, double value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, bool value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, DateTime value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, object value) { - return this.Insert(nIndex, STJson.FromObject(value)); - } - - public STJson Insert(int nIndex, STJson json) { - if (this.ValueType != STJsonValueType.Object) { - throw new STJsonException("Current STJson is not Array."); - } - m_lst_values.Insert(nIndex, json); - return this; - } - - public STJson RemoveAt(int nIndex) { - if (this.ValueType != STJsonValueType.Object) { - throw new STJsonException("Current STJson is not Array."); - } - var json = m_lst_values[nIndex]; - m_lst_values.RemoveAt(nIndex); - return json; - } - - public void Clear() { - if (m_dic_values != null) { - m_dic_values.Clear(); - } - if (m_lst_values != null) { - m_lst_values.Clear(); - } - this.Value = null; - } - - public void SetValue(string strText) { - this.Value = strText; - this.SetModel(STJsonValueType.String); - } - - public void SetValue(int number) { - this.Value = number; - this.SetModel(STJsonValueType.Long); - } - - public void SetValue(long number) { - this.Value = number; - this.SetModel(STJsonValueType.Long); - } - - public void SetValue(float number) { - this.Value = number; - this.SetModel(STJsonValueType.Double); - } - - - public void SetValue(double number) { - this.Value = number; - this.SetModel(STJsonValueType.Double); - } - - public void SetValue(bool boolean) { - this.Value = boolean; - this.SetModel(STJsonValueType.Boolean); - } - - public void SetValue(DateTime dateTime) { - this.Value = dateTime; - this.SetModel(STJsonValueType.Datetime); - } - - public void SetValue(STJson json) { - if (json == null) { - this.SetValue(strText: null); - return; - } - this.Value = json.Value; - this.ValueType = json.ValueType; - m_dic_values = json.m_dic_values; - m_lst_values = json.m_lst_values; - } - - public void SetValue(object obj) { - this.SetValue(STJson.FromObject(obj)); - } - - public override string ToString() { - StringBuilder sb = new StringBuilder(); - this.ToStringPrivate(sb); - return sb.ToString(); - } - - public string ToString(int nSpaceCount) { - string strJson = this.ToString(); - return STJson.Format(strJson, nSpaceCount); - } - - private void ToStringPrivate(StringBuilder sb) { - switch (this.ValueType) { - case STJsonValueType.Long: - case STJsonValueType.Double: - sb.Append(this.Value.ToString()); - break; - case STJsonValueType.String: - if (this.Value == null) { - sb.Append("null"); - } else { - sb.Append('\"'); - sb.Append(STJson.Escape(this.Value.ToString())); - sb.Append('\"'); - } - break; - case STJsonValueType.Boolean: - sb.Append(true.Equals(this.Value) ? "true" : "false"); - break; - case STJsonValueType.Array: - if (m_lst_values == null) { - sb.Append("null"); - break; - } - sb.Append('['); - foreach (var v in m_lst_values) { - if (v == null) { - sb.Append("null"); - } else { - v.ToStringPrivate(sb); - } - sb.Append(','); - } - if (m_lst_values.Count == 0) { - sb.Append(']'); - } else { - sb[sb.Length - 1] = ']'; - } - break; - case STJsonValueType.Datetime: - sb.Append('\"'); - sb.Append(((DateTime)this.Value).ToString("O")); - sb.Append('\"'); - break; - case STJsonValueType.Object: - if (m_dic_values == null) { - sb.Append("null"); - break; - } - sb.Append('{'); - foreach (var v in m_dic_values) { - sb.Append('\"'); - sb.Append(STJson.Escape(v.Key.ToString())); - sb.Append("\":"); - v.Value.ToStringPrivate(sb); - sb.Append(','); - } - if (m_dic_values.Count == 0) { - sb.Append('}'); - } else { - sb[sb.Length - 1] = '}'; - } - break; - default: - sb.Append("{}"); - break; - } - } - - public IEnumerator GetEnumerator() { - switch (this.ValueType) { - case STJsonValueType.Array: - foreach (var v in m_lst_values) { - yield return v; - } - break; - case STJsonValueType.Object: - foreach (var v in m_dic_values) { - yield return v.Value; - } - break; - } - } - - IEnumerator IEnumerable.GetEnumerator() { - return this.GetEnumerator(); - } - - public STJson Clone() { - STJson json = new STJson(); - json.Value = this.Value; - json.ValueType = this.ValueType; - if (m_dic_values != null && m_dic_values.Count > 0) { - if (json.m_dic_values == null) { - json.m_dic_values = new Dictionary(); - } - foreach (var v in m_dic_values) { - json.m_dic_values.Add(v.Key, v.Value.Clone()); - } - } - if (m_lst_values != null && m_lst_values.Count > 0) { - if (json.m_lst_values == null) { - json.m_lst_values = new List(); - } - foreach (var v in m_lst_values) { - json.m_lst_values.Add(v.Clone()); - } - } - return json; - } - - object ICloneable.Clone() { - return this.Clone(); - } - - internal void SetModel(STJsonValueType valueType) { - if (this.ValueType == valueType) return; - this.ValueType = valueType; - switch (valueType) { - case STJsonValueType.Long: - case STJsonValueType.Double: - case STJsonValueType.String: - case STJsonValueType.Boolean: - case STJsonValueType.Datetime: - m_dic_values = null; - m_lst_values = null; - break; - case STJsonValueType.Array: - m_dic_values = null; - if (m_lst_values == null) { - m_lst_values = new List(); - } - break; - case STJsonValueType.Object: - m_lst_values = null; - if (m_dic_values == null) { - m_dic_values = new Dictionary(); - } - break; - default:// case STJsonValueType.Undefined: - this.Value = null; - m_dic_values = null; - m_lst_values = null; - break; - } - } - - internal void SetArray(List lst) { - this.SetModel(STJsonValueType.Array); - m_lst_values = lst; - } - } -} - diff --git a/Src/STLib.Json/Attributes/STJsonAttribute.cs b/Src/STLib.Json/STJson/Attributes/STJsonAttribute.cs similarity index 53% rename from Src/STLib.Json/Attributes/STJsonAttribute.cs rename to Src/STLib.Json/STJson/Attributes/STJsonAttribute.cs index 5a6dbdc..a28ec17 100644 --- a/Src/STLib.Json/Attributes/STJsonAttribute.cs +++ b/Src/STLib.Json/STJson/Attributes/STJsonAttribute.cs @@ -4,9 +4,9 @@ namespace STLib.Json { public class STJsonAttribute : Attribute { - public STJsonSerilizaMode SerilizaMode { get; private set; } + public STJsonSerializeMode SerilizaMode { get; private set; } - public STJsonAttribute(STJsonSerilizaMode serilizaMode) { + public STJsonAttribute(STJsonSerializeMode serilizaMode) { this.SerilizaMode = serilizaMode; } } diff --git a/Src/STLib.Json/Attributes/STJsonConverterAttribute.cs b/Src/STLib.Json/STJson/Attributes/STJsonConverterAttribute.cs similarity index 100% rename from Src/STLib.Json/Attributes/STJsonConverterAttribute.cs rename to Src/STLib.Json/STJson/Attributes/STJsonConverterAttribute.cs diff --git a/Src/STLib.Json/Attributes/STJsonPropertyAttribute.cs b/Src/STLib.Json/STJson/Attributes/STJsonPropertyAttribute.cs similarity index 100% rename from Src/STLib.Json/Attributes/STJsonPropertyAttribute.cs rename to Src/STLib.Json/STJson/Attributes/STJsonPropertyAttribute.cs diff --git a/Src/STLib.Json/Converter/FPInfo.cs b/Src/STLib.Json/STJson/Converter/FPInfo.cs similarity index 86% rename from Src/STLib.Json/Converter/FPInfo.cs rename to Src/STLib.Json/STJson/Converter/FPInfo.cs index d4f8620..8b7da0c 100644 --- a/Src/STLib.Json/Converter/FPInfo.cs +++ b/Src/STLib.Json/STJson/Converter/FPInfo.cs @@ -78,12 +78,12 @@ public Attribute GetCustomAttribute(Type type) { public static List GetFPInfo(Type type) { List lst = null; - int nCode = type.GetHashCode(); - if (m_dic_fp_infos.ContainsKey(nCode)) { - return m_dic_fp_infos[nCode]; + int n_code = type.GetHashCode(); + if (m_dic_fp_infos.ContainsKey(n_code)) { + return m_dic_fp_infos[n_code]; } else { lst = new List(); - m_dic_fp_infos.Add(nCode, lst); + m_dic_fp_infos.Add(n_code, lst); } var fs = type.GetFields(BindingFlags.Instance | BindingFlags.Public); var ps = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); @@ -103,12 +103,12 @@ public static List GetFPInfo(Type type) { fp.Type = v.PropertyType; fp.CanGetValue = v.GetGetMethod() != null; fp.CanSetValue = v.GetSetMethod() != null; - if (fp.CanGetValue) { - fp.CanGetValue = v.GetGetMethod().GetParameters().Length == 0; - } - if (fp.CanSetValue) { - fp.CanSetValue = v.GetSetMethod().GetParameters().Length == 0; - } + //if (fp.CanGetValue) { + // fp.CanGetValue = v.GetGetMethod().GetParameters().Length == 0; + //} + //if (fp.CanSetValue) { + // fp.CanSetValue = v.GetSetMethod().GetParameters().Length == 0; + //} lst.Add(fp); } for (int i = 0; i < lst.Count; i++) { @@ -122,16 +122,18 @@ public static List GetFPInfo(Type type) { } else { fp.KeyName = fp.Name; } + STJsonConverter converter = null; attr = fp.GetCustomAttribute(m_type_attr_converter); if (attr == null) { continue; } - nCode = attr.GetType().GetHashCode(); - if (m_dic_type_map.ContainsKey(nCode)) { - converter = m_dic_type_map[nCode]; + n_code = attr.GetType().GetHashCode(); + if (m_dic_type_map.ContainsKey(n_code)) { + converter = m_dic_type_map[n_code]; } else { converter = (STJsonConverter)Activator.CreateInstance(((STJsonConverterAttribute)attr).Type); + m_dic_type_map.Add(n_code, converter); } fp.Converter = converter; } diff --git a/Src/STLib.Json/Converter/ObjectToSTJson.cs b/Src/STLib.Json/STJson/Converter/ObjectToSTJson.cs similarity index 81% rename from Src/STLib.Json/Converter/ObjectToSTJson.cs rename to Src/STLib.Json/STJson/Converter/ObjectToSTJson.cs index 78d7271..e4025b0 100644 --- a/Src/STLib.Json/Converter/ObjectToSTJson.cs +++ b/Src/STLib.Json/STJson/Converter/ObjectToSTJson.cs @@ -9,22 +9,24 @@ internal class ObjectToSTJson { private static Type m_type_attr_stjson = typeof(STJsonAttribute); - public static STJson Get(object obj, STJsonSetting setting) { + public static STJson Get(object obj, STJsonSetting setting) + { if (obj == null) { return null; } if (obj is STJson) return (STJson)obj; - var t = obj.GetType(); + var type = obj.GetType(); STJson json = new STJson(); - bool bProcessed = true; - STJsonConverter converter = STJson.GetConverter(t); + bool b_processed = true; + STJsonConverter converter = STJson.GetCustomConverter(type); + if (converter == null) converter = STJson.GetConverter(type); if (converter != null) { - var json_custom = converter.ObjectToJson(t, obj, ref bProcessed); - if (bProcessed) { + var json_custom = converter.ObjectToJson(type, obj, ref b_processed); + if (b_processed) { return json_custom; } } - if (t.IsEnum) { + if (type.IsEnum) { if (setting.EnumUseNumber) { json.SetValue(Convert.ToInt64(obj)); } else { @@ -33,9 +35,9 @@ public static STJson Get(object obj, STJsonSetting setting) { return json; } - if (t.IsArray) { + if (type.IsArray) { Array arr = obj as Array; - int nDim = t.FullName.Length - t.FullName.LastIndexOf('[') - 1; + int nDim = type.FullName.Length - type.FullName.LastIndexOf('[') - 1; int[] nLens = new int[nDim]; int[] nIndices = new int[nDim]; for (int i = 0; i < nDim; i++) { @@ -44,7 +46,7 @@ public static STJson Get(object obj, STJsonSetting setting) { json.SetValue(GetArray(arr, nLens, nIndices, 0, setting)); return json; } - if (t.IsGenericType) { + if (type.IsGenericType) { if (obj is IDictionary) { var idic = (IDictionary)obj; json.SetModel(STJsonValueType.Object); @@ -70,8 +72,8 @@ public static STJson Get(object obj, STJsonSetting setting) { } } json.SetModel(STJsonValueType.Object); - var fps = FPInfo.GetFPInfo(t); - var serilizaModel = STJsonSerilizaMode.All; + var fps = FPInfo.GetFPInfo(type); + var serilizaModel = STJsonSerializeMode.All; if (!setting.IgnoreAttribute) { //#if NETSTANDARD // var attr = t.GetCustomAttribute(m_type_attr_stjson); @@ -79,7 +81,7 @@ public static STJson Get(object obj, STJsonSetting setting) { // serilizaModel = ((STJsonAttribute)attr).SerilizaModel; // } //#else - var attrs = t.GetCustomAttributes(m_type_attr_stjson, true); + var attrs = type.GetCustomAttributes(m_type_attr_stjson, true); if (attrs != null && attrs.Length > 0) { serilizaModel = ((STJsonAttribute)attrs[0]).SerilizaMode; } @@ -90,20 +92,20 @@ public static STJson Get(object obj, STJsonSetting setting) { continue; } switch (serilizaModel) { - case STJsonSerilizaMode.All: + case STJsonSerializeMode.All: break; - case STJsonSerilizaMode.Include: + case STJsonSerializeMode.Include: if (p.PropertyAttribute == null) { continue; } break; - case STJsonSerilizaMode.Exclude: + case STJsonSerializeMode.Exclude: if (p.PropertyAttribute != null) { continue; } break; } - switch (setting.KyeMode) { + switch (setting.Mode) { case STJsonSetting.KeyMode.Include: if (!setting.KeyList.Contains(p.KeyName)) { continue; @@ -120,9 +122,9 @@ public static STJson Get(object obj, STJsonSetting setting) { converter = STJson.GetCustomConverter(p.KeyName); } if (converter != null) { - bProcessed = true; - var json_custom = converter.ObjectToJson(t, p.GetValue(obj), ref bProcessed); - if (bProcessed) { + b_processed = true; + var json_custom = converter.ObjectToJson(type, p.GetValue(obj), ref b_processed); + if (b_processed) { return json.SetItem(p.KeyName, json_custom); } } @@ -132,7 +134,8 @@ public static STJson Get(object obj, STJsonSetting setting) { return json; } - private static STJson GetArray(Array arr, int[] nLens, int[] nIndices, int nLevel, STJsonSetting setting) { + private static STJson GetArray(Array arr, int[] nLens, int[] nIndices, int nLevel, STJsonSetting setting) + { STJson json = new STJson(); json.SetModel(STJsonValueType.Array); for (int i = 0; i < nLens[nLevel]; i++) { diff --git a/Src/STLib.Json/STJson/Converter/ObjectToString.cs b/Src/STLib.Json/STJson/Converter/ObjectToString.cs new file mode 100644 index 0000000..7278247 --- /dev/null +++ b/Src/STLib.Json/STJson/Converter/ObjectToString.cs @@ -0,0 +1,397 @@ +using System; +using System.IO; +using System.Collections; + +using ME = STLib.Json.ObjectToString; + +namespace STLib.Json +{ + internal class ObjectToString + { + private static Type m_type_json = typeof(STJson); + private static Type m_type_attr_stjson = typeof(STJsonAttribute); + + public static void Get(TextWriter writer, int n_level, int n_space_count, object obj, STJsonSetting setting) + { + if (obj == null) { + writer.Write("null"); + return; + } + var type = obj.GetType(); + if (type == m_type_json) { + ((STJson)obj).ToString(writer, n_level, n_space_count); + return; + } + bool bProcessed = true; + STJsonConverter converter = STJson.GetCustomConverter(type); + if (converter == null) converter = STJson.GetConverter(type); + if (converter != null) { + var str = converter.ObjectToString(type, obj, ref bProcessed); + if (bProcessed) { + writer.Write(STJson.Format(str, n_space_count, "".PadLeft(n_level * n_space_count))); + return; + } + } + if (type.IsEnum) { + if (setting.EnumUseNumber) { + writer.Write((Convert.ToInt64(obj)).ToString()); + } else { + writer.Write('\"'); + writer.Write(Convert.ToString(obj)); + writer.Write('\"'); + } + return; + } + + if (type.IsArray) { + Array arr = obj as Array; + var str_name = type.FullName; + int n_dim = str_name.Length - str_name.LastIndexOf('[') - 1; + int[] nLens = new int[n_dim]; + int[] nIndices = new int[n_dim]; + for (int i = 0; i < n_dim; i++) { + nLens[i] = arr.GetLength(i); + } + ME.GetArray(writer, n_level + 1, n_space_count, arr, nLens, nIndices, 0, setting); + return; + } + + var n_counter = 0; + var str_spance_base = "".PadLeft(n_level * n_space_count); + var str_spance_inc = "".PadLeft((n_level + 1) * n_space_count); + + if (type.IsGenericType) { + if (obj is IDictionary) { + var idic = (IDictionary)obj; + IEnumerator ie_keys = idic.Keys.GetEnumerator(); + IEnumerator ie_values = idic.Values.GetEnumerator(); + writer.Write('{'); + while (ie_keys.MoveNext() && ie_values.MoveNext()) { + if (n_counter++ != 0) writer.Write(','); + var str_key = ie_keys.Current.ToString(); + if (n_space_count == 0) { + writer.Write('\"'); + writer.Write(STJson.Escape(str_key)); + writer.Write("\":"); + } else { + writer.Write("\r\n" + str_spance_inc + "\""); + writer.Write(STJson.Escape(str_key)); + writer.Write("\": "); + } + ME.Get(writer, n_level + 1, n_space_count, ie_values.Current, setting); + } + if (n_counter == 0) { + writer.Write('}'); + } else { + writer.Write(n_space_count == 0 ? "}" : ("\r\n" + str_spance_base + "}")); + } + return; + } + + if (obj is IEnumerable) { + IEnumerator ie = ((IEnumerable)obj).GetEnumerator(); + writer.Write('['); + while (ie.MoveNext()) { + if (n_counter++ != 0) writer.Write(','); + if (n_space_count != 0) { + writer.Write("\r\n" + str_spance_inc); + } + ObjectToString.Get(writer, n_level + 1, n_space_count, ie.Current, setting); + } + if (n_counter == 0) { + writer.Write(']'); + } else { + writer.Write(n_space_count == 0 ? "]" : ("\r\n" + str_spance_base + "]")); + } + return; + } + } + + var fps = FPInfo.GetFPInfo(type); + var serilizaModel = STJsonSerializeMode.All; + if (!setting.IgnoreAttribute) { + var attrs = type.GetCustomAttributes(m_type_attr_stjson, true); + if (attrs != null && attrs.Length > 0) { + serilizaModel = ((STJsonAttribute)attrs[0]).SerilizaMode; + } + } + n_counter = 0; + writer.Write('{'); + foreach (var p in fps) { + if (!p.CanGetValue) { + continue; + } + switch (serilizaModel) { + case STJsonSerializeMode.All: + break; + case STJsonSerializeMode.Include: + if (p.PropertyAttribute == null) { + continue; + } + break; + case STJsonSerializeMode.Exclude: + if (p.PropertyAttribute != null) { + continue; + } + break; + } + switch (setting.Mode) { + case STJsonSetting.KeyMode.Include: + if (!setting.KeyList.Contains(p.KeyName)) { + continue; + } + break; + case STJsonSetting.KeyMode.Exclude: + if (setting.KeyList.Contains(p.KeyName)) { + continue; + } + break; + } + var val = p.GetValue(obj); + if (val == null && setting.IgnoreNullValue) { + continue; + } + if (n_counter++ != 0) writer.Write(','); + if (n_space_count == 0) { + writer.Write('\"'); + writer.Write(p.KeyName); + writer.Write("\":"); + } else { + writer.Write("\r\n" + str_spance_inc + "\""); + writer.Write(p.KeyName); + writer.Write("\": "); + } + + bProcessed = true; + converter = p.Converter; + if (converter == null) { + converter = STJson.GetCustomConverter(p.KeyName); + } + if (converter != null) { + var str = converter.ObjectToString(type, val, ref bProcessed); + if (bProcessed) { + writer.Write(STJson.Format(str, n_space_count, str_spance_base)); + } + } else { + ME.Get(writer, n_level + 1, n_space_count, val, setting); + } + } + if (n_counter == 0) { + writer.Write('}'); + } else { + writer.Write(n_space_count == 0 ? "}" : ("\r\n" + str_spance_base + "}")); + } + } + + private static void GetArray(TextWriter writer, int n_level_space, int n_space_count, Array arr, int[] arr_n_len, int[] arr_n_index, int n_level_arr, STJsonSetting setting) + { + int n_len = arr_n_len[n_level_arr]; + if (n_len == 0) { + writer.Write("[]"); + return; + } + var str_spance_base = "".PadLeft(n_level_space * n_space_count); + var str_spance_inc = "".PadLeft((n_level_space - 1) * n_space_count); + writer.Write('['); + for (int i = 0; i < n_len; i++) { + if (i != 0) writer.Write(','); + if (n_space_count != 0) { + writer.Write("\r\n" + str_spance_base); + } + arr_n_index[n_level_arr] = i; + if (n_level_arr == arr_n_len.Length - 1) { + var obj = arr.GetValue(arr_n_index); + ME.Get(writer, n_level_space + 0, n_space_count, obj, setting); + } else { + ME.GetArray(writer, n_level_space + 1, n_space_count, arr, arr_n_len, arr_n_index, n_level_arr + 1, setting); + } + } + if (n_len == 0) { + writer.Write(']'); + } else { + writer.Write(n_space_count == 0 ? "]" : ("\r\n" + str_spance_inc + "]")); + } + } + + // ========================================== + + //public static void Get(StringBuilder sb, object obj, STJsonSetting setting) + //{ + // if (obj == null) { + // sb.Append("null"); + // return; + // } + // var t = obj.GetType(); + // if (t == m_type_json) { + // sb.Append(obj.ToString()); + // return; + // } + // bool bProcessed = true; + // STJsonConverter converter = STJson.GetConverter(t); + // if (converter != null) { + // var str = converter.ObjectToString(t, obj, ref bProcessed); + // if (bProcessed) { + // sb.Append(str); + // return; + // } + // } + // if (t.IsEnum) { + // if (setting.EnumUseNumber) { + // sb.Append((Convert.ToInt64(obj)).ToString()); + // } else { + // sb.Append('\"'); + // sb.Append(Convert.ToString(obj)); + // sb.Append('\"'); + // } + // return; + // } + + // if (t.IsArray) { + // Array arr = obj as Array; + // var strName = t.FullName; + // int nDim = strName.Length - strName.LastIndexOf('[') - 1; + // int[] nLens = new int[nDim]; + // int[] nIndices = new int[nDim]; + // for (int i = 0; i < nDim; i++) { + // nLens[i] = arr.GetLength(i); + // } + // ME.GetArray(sb, arr, nLens, nIndices, 0, setting); + // return; + // } + + // if (t.IsGenericType) { + // if (obj is IDictionary) { + // var idic = (IDictionary)obj; + // IEnumerator ie_keys = idic.Keys.GetEnumerator(); + // IEnumerator ie_values = idic.Values.GetEnumerator(); + // sb.Append('{'); + // while (ie_keys.MoveNext() && ie_values.MoveNext()) { + // var strKey = ie_keys.Current.ToString(); + // sb.Append('\"'); + // sb.Append(STJson.Escape(strKey)); + // sb.Append("\":"); + // ME.Get(sb, ie_values.Current, setting); + // sb.Append(','); + // } + // ME.CheckEnd(sb, '}'); + // return; + // } + + // if (obj is IEnumerable) { + // IEnumerator ie = ((IEnumerable)obj).GetEnumerator(); + // sb.Append('['); + // while (ie.MoveNext()) { + // ObjectToString.Get(sb, ie.Current, setting); + // sb.Append(','); + // } + // ME.CheckEnd(sb, ']'); + // return; + // } + // } + + // var fps = FPInfo.GetFPInfo(t); + // var serilizaModel = STJsonSerilizaMode.All; + // if (!setting.IgnoreAttribute) { + // //#if NETSTANDARD + // // var attr = t.GetCustomAttribute(m_type_attr_stjson); + // // if (attr != null) { + // // serilizaModel = ((STJsonAttribute)attr).SerilizaModel; + // // } + // //#else + // var attrs = t.GetCustomAttributes(m_type_attr_stjson, true); + // if (attrs != null && attrs.Length > 0) { + // serilizaModel = ((STJsonAttribute)attrs[0]).SerilizaMode; + // } + // //#endif + // } + // sb.Append('{'); + // foreach (var p in fps) { + // if (!p.CanGetValue) { + // continue; + // } + // switch (serilizaModel) { + // case STJsonSerilizaMode.All: + // break; + // case STJsonSerilizaMode.Include: + // if (p.PropertyAttribute == null) { + // continue; + // } + // break; + // case STJsonSerilizaMode.Exclude: + // if (p.PropertyAttribute != null) { + // continue; + // } + // break; + // } + // switch (setting.Mode) { + // case STJsonSetting.KeyMode.Include: + // if (!setting.KeyList.Contains(p.KeyName)) { + // continue; + // } + // break; + // case STJsonSetting.KeyMode.Exclude: + // if (setting.KeyList.Contains(p.KeyName)) { + // continue; + // } + // break; + // } + // bProcessed = true; + // converter = p.Converter; + // if (converter == null) { + // converter = STJson.GetCustomConverter(p.KeyName); + // } + // if (converter != null) { + // var vul = p.GetValue(obj); + // if (vul == null && setting.IgnoreNullValue) { + // continue; + // } + // var str = converter.ObjectToString(t, vul, ref bProcessed); + // if (bProcessed) { + // sb.Append('\"'); + // sb.Append(p.KeyName); + // sb.Append("\":"); + // sb.Append(str); + // } + // } else { + // if (!p.CanGetValue) continue; + // var vul = p.GetValue(obj); + // if (vul == null && setting.IgnoreNullValue) { + // continue; + // } + // sb.Append('\"'); + // sb.Append(p.KeyName); + // sb.Append("\":"); + // ME.Get(sb, vul, setting); + // } + // sb.Append(','); + // } + // ME.CheckEnd(sb, '}'); + //} + + //private static void GetArray(StringBuilder sb, Array arr, int[] nLens, int[] nIndices, int nLevel, STJsonSetting setting) + //{ + // sb.Append('['); + // for (int i = 0; i < nLens[nLevel]; i++) { + // nIndices[nLevel] = i; + // if (nLevel == nLens.Length - 1) { + // var obj = arr.GetValue(nIndices); + // ME.Get(sb, obj, setting); + // } else { + // ME.GetArray(sb, arr, nLens, nIndices, nLevel + 1, setting); + // } + // sb.Append(','); + // } + // ME.CheckEnd(sb, ']'); + //} + + //private static void CheckEnd(StringBuilder sb, char ch) + //{ + // if (sb[sb.Length - 1] == ',') { + // sb[sb.Length - 1] = ch; + // } else { + // sb.Append(ch); + // } + //} + } +} + diff --git a/Src/STLib.Json/Converter/STJsonBuildInConverter.cs b/Src/STLib.Json/STJson/Converter/STJsonBuildInConverter.cs similarity index 81% rename from Src/STLib.Json/Converter/STJsonBuildInConverter.cs rename to Src/STLib.Json/STJson/Converter/STJsonBuildInConverter.cs index b314e08..61fd376 100644 --- a/Src/STLib.Json/Converter/STJsonBuildInConverter.cs +++ b/Src/STLib.Json/STJson/Converter/STJsonBuildInConverter.cs @@ -10,7 +10,8 @@ internal class STJsonBuildInConverter { private static Dictionary m_dic_type_map; - static STJsonBuildInConverter() { + static STJsonBuildInConverter() + { m_dic_type_map = new Dictionary(); m_dic_type_map.Add(typeof(byte).GetHashCode(), new ByteConverter()); m_dic_type_map.Add(typeof(sbyte).GetHashCode(), new SByteConverter()); @@ -37,12 +38,14 @@ static STJsonBuildInConverter() { m_dic_type_map.Add(typeof(DataTable).GetHashCode(), new DateTableConverter()); } - public static STJsonConverter Get(Type type) { + public static STJsonConverter Get(Type type) + { int nCode = type.GetHashCode(); return STJsonBuildInConverter.Get(nCode); } - public static STJsonConverter Get(int nCode) { + public static STJsonConverter Get(int nCode) + { if (m_dic_type_map.ContainsKey(nCode)) { return m_dic_type_map[nCode]; } @@ -51,402 +54,468 @@ public static STJsonConverter Get(int nCode) { public class ByteConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToByte(json.Value); } } public class SByteConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToSByte(json.Value); } } public class ShortConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToInt16(json.Value); } } public class UShortConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToUInt16(json.Value); } } public class IntConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToInt32(json.Value); } } public class UIntConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToUInt32(json.Value); } } public class LongConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToInt64(json.Value); } } public class ULongConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToInt64(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToUInt64(json.Value); } } public class FloatConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToSingle(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToSingle(json.Value); } } public class DoubleConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToDouble(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToDouble(json.Value); } } public class DecimalConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return Convert.ToString(obj); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToDouble(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToDecimal(json.Value); } } public class BoolConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return obj == null ? "false" : Convert.ToString(obj).ToLower(); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToBoolean(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToBoolean(json.Value); } } public class CharConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return "\"" + STJson.Escape(Convert.ToString(obj)) + "\""; } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToString(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToChar(json.Value); } } public class StringConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return "\"" + STJson.Escape(Convert.ToString(obj)) + "\""; } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); json.SetValue(Convert.ToString(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToString(json.Value); } } public class PointConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var point = (Point)obj; return string.Format("[{0},{1}]", point.X, point.Y); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var point = (Point)obj; return new STJson().Append(point.X, point.Y); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return new Point( - json[0].GetValue(), - json[1].GetValue() + Convert.ToInt32(json[0].Value), + Convert.ToInt32(json[1].Value) ); } } public class PointFConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var point = (PointF)obj; return string.Format("[{0},{1}]", point.X, point.Y); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var point = (PointF)obj; return new STJson().Append(point.X, point.Y); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return new PointF( - json[0].GetValue(), - json[1].GetValue() + Convert.ToSingle(json[0].Value), + Convert.ToSingle(json[1].Value) ); } } public class SizeConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var size = (Size)obj; return string.Format("[{0},{1}]", size.Width, size.Width); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var size = (Size)obj; return new STJson().Append(size.Width, size.Width); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return new Size( - json[0].GetValue(), - json[1].GetValue() + Convert.ToInt32(json[0].Value), + Convert.ToInt32(json[1].Value) ); } } public class SizeFConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var size = (SizeF)obj; return string.Format("[{0},{1}]", size.Width, size.Width); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var size = (SizeF)obj; return new STJson().Append(size.Width, size.Width); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return new SizeF( - json[0].GetValue(), - json[1].GetValue() + Convert.ToSingle(json[0].Value), + Convert.ToSingle(json[1].Value) ); } } public class RectangleConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var rect = (Rectangle)obj; return string.Format("[{0},{1},{2},{3}]", rect.X, rect.Y, rect.Width, rect.Height); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var rect = (Rectangle)obj; return new STJson().Append(rect.X, rect.Y, rect.Width, rect.Height); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return new Rectangle( - json[0].GetValue(), - json[1].GetValue(), - json[2].GetValue(), - json[3].GetValue() + Convert.ToInt32(json[0].Value), + Convert.ToInt32(json[1].Value), + Convert.ToInt32(json[2].Value), + Convert.ToInt32(json[3].Value) ); } } public class RectangleFConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var rect = (RectangleF)obj; return string.Format("[{0},{1},{2},{3}]", rect.X, rect.Y, rect.Width, rect.Height); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var rect = (RectangleF)obj; return new STJson().Append(rect.X, rect.Y, rect.Width, rect.Height); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return new RectangleF( - json[0].GetValue(), - json[1].GetValue(), - json[2].GetValue(), - json[3].GetValue() + Convert.ToSingle(json[0].Value), + Convert.ToSingle(json[1].Value), + Convert.ToSingle(json[2].Value), + Convert.ToSingle(json[3].Value) ); } } public class ColorConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var color = (Color)obj; - return string.Format("[{0},{1},{3},{4}]", color.A, color.R, color.G, color.B); + return string.Format("[{0},{1},{2},{3}]", color.A, color.R, color.G, color.B); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { var color = (Color)obj; return new STJson().Append(color.A, color.R, color.G, color.B); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Color.FromArgb( - json[0].GetValue(), - json[1].GetValue(), - json[2].GetValue(), - json[3].GetValue() + Convert.ToInt32(json[0].Value), + Convert.ToInt32(json[1].Value), + Convert.ToInt32(json[2].Value), + Convert.ToInt32(json[3].Value) ); } } public class DateTimeConverter : STJsonConverter { - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { return "\"" + (obj == null ? "1970-01-01T00:00:00.0000+00:00" : ((DateTime)obj).ToString("O")) + "\""; } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = new STJson(); //json.SetValue((obj == null ? "1970-01-01T00:00:00.0000+00:00" : ((DateTime)obj).ToString("O"))); json.SetValue(Convert.ToDateTime(obj)); return json; } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { return Convert.ToDateTime(json.Value); } } @@ -455,12 +524,14 @@ public class DateTableConverter : STJsonConverter { private static Type m_type_object = typeof(object); - public override string ObjectToString(Type t, object obj, ref bool bProcessed) { + public override string ObjectToString(Type t, object obj, ref bool bProcessed) + { var json = this.ObjectToJson(t, obj, ref bProcessed); return json == null ? null : json.ToString(); } - public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { + public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) + { STJson json = STJson.CreateArray(); DataTable dt = obj as DataTable; if (dt == null) { @@ -486,7 +557,8 @@ public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) { .SetItem("rows", json_rows); } - public override object JsonToObject(Type t, STJson json, ref bool bProcessed) { + public override object JsonToObject(Type t, STJson json, ref bool bProcessed) + { DataTable dt = new DataTable(); foreach (var c in json["columns"]) { DataColumn dc = new DataColumn(c["name"].GetValue(), m_type_object); diff --git a/Src/STLib.Json/Converter/STJsonConverter.cs b/Src/STLib.Json/STJson/Converter/STJsonConverter.cs similarity index 100% rename from Src/STLib.Json/Converter/STJsonConverter.cs rename to Src/STLib.Json/STJson/Converter/STJsonConverter.cs diff --git a/Src/STLib.Json/Converter/STJsonToObject.cs b/Src/STLib.Json/STJson/Converter/STJsonToObject.cs similarity index 95% rename from Src/STLib.Json/Converter/STJsonToObject.cs rename to Src/STLib.Json/STJson/Converter/STJsonToObject.cs index caee086..e3ed92b 100644 --- a/Src/STLib.Json/Converter/STJsonToObject.cs +++ b/Src/STLib.Json/STJson/Converter/STJsonToObject.cs @@ -21,7 +21,7 @@ public static T Get(STJson json, STJsonSetting setting) { public static void SetObject(STJson json, object obj, STJsonSetting setting) { var t = obj.GetType(); - var serilizaModel = STJsonSerilizaMode.All; + var serilizaModel = STJsonSerializeMode.All; if (!setting.IgnoreAttribute) { #if NETSTANDARD var attr = t.GetCustomAttribute(m_type_attr_stjson); @@ -42,20 +42,20 @@ public static void SetObject(STJson json, object obj, STJsonSetting setting) { continue; } switch (serilizaModel) { - case STJsonSerilizaMode.All: + case STJsonSerializeMode.All: break; - case STJsonSerilizaMode.Include: + case STJsonSerializeMode.Include: if (p.PropertyAttribute == null) { continue; } break; - case STJsonSerilizaMode.Exclude: + case STJsonSerializeMode.Exclude: if (p.PropertyAttribute != null) { continue; } break; } - switch (setting.KyeMode) { + switch (setting.Mode) { case STJsonSetting.KeyMode.Include: if (!setting.KeyList.Contains(p.KeyName)) { continue; @@ -85,7 +85,11 @@ public static void SetObject(STJson json, object obj, STJsonSetting setting) { } } - private static object GetObjectValue(STJson json, Type t, STJsonSetting setting) { + private static object GetObjectValue(STJson json, Type t, STJsonSetting setting) + { + if (json == null || json.IsNullValue) { + return null; + } object obj = null; bool bProcessed = true; STJsonConverter converter = STJson.GetConverter(t); @@ -95,9 +99,6 @@ private static object GetObjectValue(STJson json, Type t, STJsonSetting setting) return obj; } } - if (json == null || json.IsNullObject) { - return null; - } if (t == m_type_object) { if (json.Value != null) return json.Value; if (json.ValueType == STJsonValueType.Object) { @@ -196,7 +197,7 @@ private static object GetObjectValue(STJson json, Type t, STJsonSetting setting) } return obj; } - var serilizaModel = STJsonSerilizaMode.All; + var serilizaModel = STJsonSerializeMode.All; if (!setting.IgnoreAttribute) { #if NETSTANDARD var attr = t.GetCustomAttribute(m_type_attr_stjson); @@ -215,20 +216,20 @@ private static object GetObjectValue(STJson json, Type t, STJsonSetting setting) if (json[p.KeyName] == null) continue; if (!p.CanSetValue) continue; switch (serilizaModel) { - case STJsonSerilizaMode.All: + case STJsonSerializeMode.All: break; - case STJsonSerilizaMode.Include: + case STJsonSerializeMode.Include: if (p.PropertyAttribute == null) { continue; } break; - case STJsonSerilizaMode.Exclude: + case STJsonSerializeMode.Exclude: if (p.PropertyAttribute != null) { continue; } break; } - switch (setting.KyeMode) { + switch (setting.Mode) { case STJsonSetting.KeyMode.Include: if (!setting.KeyList.Contains(p.KeyName)) { continue; diff --git a/Src/STLib.Json/STJson/Entities/Enums/STJsonSerializeMode.cs b/Src/STLib.Json/STJson/Entities/Enums/STJsonSerializeMode.cs new file mode 100644 index 0000000..01b6ee1 --- /dev/null +++ b/Src/STLib.Json/STJson/Entities/Enums/STJsonSerializeMode.cs @@ -0,0 +1,7 @@ +namespace STLib.Json +{ + public enum STJsonSerializeMode + { + All, Include, Exclude + } +} diff --git a/Src/STLib.Json/STJson/Entities/Enums/STJsonValueType.cs b/Src/STLib.Json/STJson/Entities/Enums/STJsonValueType.cs new file mode 100644 index 0000000..24415f2 --- /dev/null +++ b/Src/STLib.Json/STJson/Entities/Enums/STJsonValueType.cs @@ -0,0 +1,7 @@ +namespace STLib.Json +{ + public enum STJsonValueType + { + Long, Double, Boolean, String, Array, Object, Datetime, Undefined + } +} diff --git a/Src/STLib.Json/STJson/Entities/Structures/STJsonTypeMapInfo.cs b/Src/STLib.Json/STJson/Entities/Structures/STJsonTypeMapInfo.cs new file mode 100644 index 0000000..0f5abd3 --- /dev/null +++ b/Src/STLib.Json/STJson/Entities/Structures/STJsonTypeMapInfo.cs @@ -0,0 +1,42 @@ +using System; + +namespace STLib.Json +{ + public struct STJsonTypeMapInfo : IComparable + { + public int HashCode; + public Type Type; + public STJsonConverter Converter; + + public static STJsonTypeMapInfo Empty; + + public int CompareTo(object obj) + { + return this.HashCode - ((STJsonTypeMapInfo)obj).HashCode; + } + + public static STJsonTypeMapInfo Create(Type type, STJsonConverter converter) + { + return new STJsonTypeMapInfo() + { + HashCode = type.GetHashCode(), + Type = type, + Converter = converter + }; + } + + public static STJsonTypeMapInfo Create(int n_code, STJsonConverter converter) + { + return new STJsonTypeMapInfo() + { + HashCode = n_code, + Converter = converter + }; + } + + public override string ToString() + { + return this.Type.FullName; + } + } +} diff --git a/Src/STLib.Json/STJson/Exceptions/STJsonCastException.cs b/Src/STLib.Json/STJson/Exceptions/STJsonCastException.cs new file mode 100644 index 0000000..96fbfe4 --- /dev/null +++ b/Src/STLib.Json/STJson/Exceptions/STJsonCastException.cs @@ -0,0 +1,11 @@ +using System; + +namespace STLib.Json +{ + public class STJsonCastException : InvalidCastException + { + public STJsonCastException(string str_error) : base(str_error) { } + public STJsonCastException(string str_error, Exception inner_exception) : base(str_error, inner_exception) { } + } +} + diff --git a/Src/STLib.Json/Exceptions/STJsonException.cs b/Src/STLib.Json/STJson/Exceptions/STJsonException.cs similarity index 58% rename from Src/STLib.Json/Exceptions/STJsonException.cs rename to Src/STLib.Json/STJson/Exceptions/STJsonException.cs index 8a2ceab..a2c9086 100644 --- a/Src/STLib.Json/Exceptions/STJsonException.cs +++ b/Src/STLib.Json/STJson/Exceptions/STJsonException.cs @@ -4,6 +4,6 @@ namespace STLib.Json { public class STJsonException : Exception { - public STJsonException(string strErr) : base(strErr) { } + public STJsonException(string str_error) : base(str_error) { } } } diff --git a/Src/STLib.Json/STJson/Exceptions/STJsonParseException.cs b/Src/STLib.Json/STJson/Exceptions/STJsonParseException.cs new file mode 100644 index 0000000..49659a4 --- /dev/null +++ b/Src/STLib.Json/STJson/Exceptions/STJsonParseException.cs @@ -0,0 +1,30 @@ +using System; + +namespace STLib.Json +{ + public class STJsonParseException : STJsonException + { + public int Index { get; private set; } + public int Row { get; private set; } + public int Col { get; private set; } + + internal STJsonParseException(STJsonToken token) + : base("Invalid token: '" + token.Value + "'. " + string.Format(" [index:{0}, row:{1}, col:{2}]", token.Index, token.Row, token.Col)) + { + this.Index = token.Index; + this.Row = token.Row; + this.Col = token.Col; + } + + internal STJsonParseException(int n_index, int n_row, int n_col, string str_error) + : base(str_error + " " + string.Format(" [index:{0}, row:{1}, col:{2}]", n_index, n_row, n_col)) + { + } + + internal STJsonParseException(int n_index, string str_error) : base(str_error) + { + this.Index = n_index; + } + } +} + diff --git a/Src/STLib.Json/STJson/Exceptions/STJsonWriterException.cs b/Src/STLib.Json/STJson/Exceptions/STJsonWriterException.cs new file mode 100644 index 0000000..c2799a4 --- /dev/null +++ b/Src/STLib.Json/STJson/Exceptions/STJsonWriterException.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace STLib.Json +{ + public class STJsonWriterException : STJsonException + { + public STJsonWriterException(string str_error) : base(str_error) { } + } +} diff --git a/Src/STLib.Json/STJson/Parser/Entities/Enums/STJsonTokenType.cs b/Src/STLib.Json/STJson/Parser/Entities/Enums/STJsonTokenType.cs new file mode 100644 index 0000000..efc4a99 --- /dev/null +++ b/Src/STLib.Json/STJson/Parser/Entities/Enums/STJsonTokenType.cs @@ -0,0 +1,7 @@ +namespace STLib.Json +{ + internal enum STJsonTokenType + { + None, Symbol, /*Keyword,*/ Long, Double, String, ItemSplitor, KVSplitor, ObjectStart, ObjectEnd, ArrayStart, ArrayEnd, Comment + } +} diff --git a/Src/STLib.Json/STJson/Parser/Entities/Structures/STJsonToken.cs b/Src/STLib.Json/STJson/Parser/Entities/Structures/STJsonToken.cs new file mode 100644 index 0000000..ae906a8 --- /dev/null +++ b/Src/STLib.Json/STJson/Parser/Entities/Structures/STJsonToken.cs @@ -0,0 +1,28 @@ +namespace STLib.Json +{ + internal struct STJsonToken + { + public static readonly STJsonToken None = new STJsonToken() + { + Index = -1, + Row = -1, + Col = -1, + Type = STJsonTokenType.None + }; + + public int Index; + public int Row; + public int Col; + public string Value; + public STJsonTokenType Type; + + public STJsonToken(int n_index, int n_row, int n_col, string str_value, STJsonTokenType type) + { + this.Index = n_index; + this.Row = n_row; + this.Col = n_col; + this.Value = str_value; + this.Type = type; + } + } +} diff --git a/Src/STLib.Json/STJson/Parser/STJsonParser.cs b/Src/STLib.Json/STJson/Parser/STJsonParser.cs new file mode 100644 index 0000000..5e0b4ec --- /dev/null +++ b/Src/STLib.Json/STJson/Parser/STJsonParser.cs @@ -0,0 +1,251 @@ +using System; +using System.Text; +using System.Collections.Generic; + +using ME = STLib.Json.STJsonParser; + +namespace STLib.Json +{ + internal class STJsonParser + { + public static STJson Parse(string str_json) + { + var lst_token = new STJsonTokenizer(str_json).GetTokens(); + return ME.Parse(lst_token); + } + + internal static STJson Parse(List lst_token) + { + if (lst_token.Count == 0) { + throw new STJsonParseException(-1, "Invalid JSON string."); + } + int n_index = 1; + STJson json = null; + switch (lst_token[0].Type) { + case STJsonTokenType.ObjectStart: + json = ME.GetObject(lst_token, ref n_index); + break; + case STJsonTokenType.ArrayStart: + json = ME.GetArray(lst_token, ref n_index); + break; + default: + throw new STJsonParseException(lst_token[0]); + } + if (n_index < lst_token.Count) { + throw new STJsonParseException(lst_token[n_index]); + } + return json; + } + + private static STJson GetObject(List lst_token, ref int n_index) + { + STJson json = new STJson(); + json.SetModel(STJsonValueType.Object); + while (n_index < lst_token.Count) { + var token = lst_token[n_index]; + if (token.Type == STJsonTokenType.ObjectEnd) { // '}' + n_index++; + return json; + } + token = lst_token[n_index++]; + if (token.Type != STJsonTokenType.String && token.Type != STJsonTokenType.Symbol) { + throw new STJsonParseException(token); + } + var jv = json.SetKey(token.Value); + if (n_index >= lst_token.Count) throw new STJsonParseException(-1, "Incomplete JSON string."); + token = lst_token[n_index++]; + if (token.Type != STJsonTokenType.KVSplitor) { // ':' + throw new STJsonParseException(token); + } + if (n_index >= lst_token.Count) throw new STJsonParseException(-1, "Incomplete JSON string."); + token = lst_token[n_index++]; + switch (token.Type) { + case STJsonTokenType.Symbol: + switch (token.Value) { + case "true": + jv.SetValue(true); + break; + case "false": + jv.SetValue(false); + break; + case "null": + jv.SetValue(value: null); + break; + default: + throw new STJsonParseException(token); + } + break; + case STJsonTokenType.String: + jv.SetValue(ME.ParseString(token)); + break; + case STJsonTokenType.Long: + jv.SetValue(ME.ParseNumberLong(token)); + break; + case STJsonTokenType.Double: + jv.SetValue(ME.ParseNumberDouble(token)); + break; + case STJsonTokenType.ObjectStart: + jv.SetValue(ME.GetObject(lst_token, ref n_index)); + break; + case STJsonTokenType.ArrayStart: + jv.SetValue(ME.GetArray(lst_token, ref n_index)); + break; + default: + throw new STJsonParseException(token); + } + token = lst_token[n_index++]; + switch (token.Type) { + case STJsonTokenType.ItemSplitor: continue; // ',' + case STJsonTokenType.ObjectEnd: return json; // '}' + default: + throw new STJsonParseException(token); + } + } + throw new STJsonParseException(-1, "Incomplete JSON string."); + } + + private static STJson GetArray(List lst_token, ref int n_index) + { + STJson json = new STJson(); + json.SetModel(STJsonValueType.Array); + while (n_index < lst_token.Count) { + var token = lst_token[n_index++]; + switch (token.Type) { + case STJsonTokenType.ArrayEnd: + return json; + case STJsonTokenType.String: + json.Append(ME.ParseString(token)); + break; + case STJsonTokenType.Long: + json.Append(ME.ParseNumberLong(token)); + break; + case STJsonTokenType.Double: + json.Append(ME.ParseNumberDouble(token)); + break; + case STJsonTokenType.Symbol: + switch (token.Value) { + case "true": + json.Append(true); + break; + case "false": + json.Append(false); + break; + case "null": + case "undefined": + json.Append(json: null); + break; + default: + throw new STJsonParseException(token); + } + break; + case STJsonTokenType.ObjectStart: + json.Append(ME.GetObject(lst_token, ref n_index)); + break; + case STJsonTokenType.ArrayStart: + json.Append(ME.GetArray(lst_token, ref n_index)); + break; + default: + throw new STJsonParseException(token); + } + token = lst_token[n_index++]; + switch (token.Type) { + case STJsonTokenType.ItemSplitor: continue; // ',' + case STJsonTokenType.ArrayEnd: return json; // ']' + default: + throw new STJsonParseException(token); + } + } + throw new STJsonParseException(-1, "Incomplete JSON string."); + } + + internal static long ParseNumberLong(STJsonToken token) + { + // note: -0xAAA, 0XAAA + var b_flag = token.Value[0] == '-'; + var str = b_flag ? token.Value.Substring(1) : token.Value; + try { + for (int i = 1; i < str.Length; i++) { + if (i > 2) break; + var ch = token.Value[i]; + switch (ch) { + case 'x': + case 'X': // -0x** will get a exception. + return b_flag ? 0 - Convert.ToInt64(str, 16) : Convert.ToInt64(str, 16); + } + } + return b_flag ? 0 - Convert.ToInt64(str) : Convert.ToInt64(str); + } catch { + throw new STJsonParseException(token); + } + } + + internal static double ParseNumberDouble(STJsonToken token) + { + try { + return Convert.ToDouble(token.Value); + } catch { + throw new STJsonParseException(token); + } + } + + internal static string ParseString(STJsonToken token) + { + int n_hex_string = 0; + string str_temp = string.Empty; + StringBuilder sb = new StringBuilder(); + + for (int i = 0, n_len = token.Value.Length; i < n_len; i++) { + var ch = token.Value[i]; + if (ch != '\\') { + sb.Append(ch); + continue; + } + if (++i >= n_len) { + throw new STJsonParseException(token); + } + ch = token.Value[i]; + var b_is_newline = false; + switch (ch) { + case '\r': + for (i++; i < n_len; i++) { + switch (token.Value[i]) { + case '\r': continue; + case '\n': b_is_newline = true; break; + default: + throw new STJsonParseException(token); + } + if (b_is_newline) break; + } + continue; + case '\n': continue; + case 'r': sb.Append('\r'); continue; + case 'n': sb.Append('\n'); continue; + case 't': sb.Append('\t'); continue; + case 'f': sb.Append('\f'); continue; + case 'b': sb.Append('\b'); continue; + case 'a': sb.Append('\a'); continue; + case 'v': sb.Append('\v'); continue; + case '0': sb.Append('\0'); continue; + case 'x': + case 'u': + n_hex_string = ch == 'x' ? 2 : 4; + if (i + n_hex_string >= token.Value.Length) { + throw new STJsonParseException(token); + } + str_temp = token.Value.Substring(i + 1, n_hex_string); + try { + sb.Append((char)Convert.ToUInt16(str_temp, 16)); + } catch { + throw new STJsonParseException(token); + } + i += n_hex_string; + continue; + default: + sb.Append(ch); + continue; + } + } + return sb.ToString(); + } + } +} diff --git a/Src/STLib.Json/STJson/Parser/STJsonTokenReader.cs b/Src/STLib.Json/STJson/Parser/STJsonTokenReader.cs new file mode 100644 index 0000000..6f6196e --- /dev/null +++ b/Src/STLib.Json/STJson/Parser/STJsonTokenReader.cs @@ -0,0 +1,316 @@ +using System; +using System.IO; +using System.Text; + +using System.Collections.Generic; +using System.Collections; + +namespace STLib.Json +{ + internal class STJsonTokenReader : IEnumerable, IDisposable + { + private TextReader m_reader; + private int m_n_position; + private int m_n_row; + private int m_n_col; + + private STJsonToken m_last_token; + private bool m_is_auto_close; + + public bool Disposed { get; private set; } + + public STJsonTokenReader(TextReader reader, bool is_auto_close) + { + m_reader = reader; + m_is_auto_close = is_auto_close; + } + + public IEnumerator GetEnumerator() + { + m_n_position = 0; + m_n_row = 1; + m_n_col = 1; + + int n_char = -1; + var token = new STJsonToken(); + + while ((n_char = m_reader.Peek()) != -1) { + var ch = (char)n_char; + int n_index = m_n_position, n_row = m_n_row, n_col = m_n_col; + if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+' || ch == '.') { + yield return m_last_token = this.GetNumber(); + continue; + } + switch (ch) { + case '{': // object start + this.ReadInt(); + yield return m_last_token = new STJsonToken(n_index, n_row, n_col, "{", STJsonTokenType.ObjectStart); + continue; + case '[': // array start + this.ReadInt(); + yield return m_last_token = new STJsonToken(n_index, n_row, n_col, "[", STJsonTokenType.ArrayStart); + continue; + case ',': // item splitor + this.ReadInt(); + yield return m_last_token = new STJsonToken(n_index, n_row, n_col, ",", STJsonTokenType.ItemSplitor); + continue; + case ':': // key value splitor + this.ReadInt(); + yield return m_last_token = new STJsonToken(n_index, n_row, n_col, ":", STJsonTokenType.KVSplitor); + continue; + case ']': // array end + this.ReadInt(); + yield return m_last_token = new STJsonToken(n_index, n_row, n_col, "]", STJsonTokenType.ArrayEnd); + continue; + case '}': // object end + this.ReadInt(); + yield return m_last_token = new STJsonToken(n_index, n_row, n_col, "}", STJsonTokenType.ObjectEnd); + continue; + case ' ': // space + case '\r': + case '\n': + case '\t': + this.ReadInt(); + continue; + case '/': + this.GetComment(); + continue; + case '\'': // string + case '\"': + token = this.GetString(); + if (m_last_token.Type == STJsonTokenType.String) { + m_last_token.Value += token.Value; + } else { + m_last_token = token; + } + while ((n_char = m_reader.Peek()) != -1) { + switch (n_char) { + case ' ': // space + case '\r': + case '\n': + case '\t': + this.ReadInt(); + continue; + } + break; + } + switch (m_reader.Peek()) { + case '\'': + case '\"': + continue; + } + yield return m_last_token; + continue; + default: + token = this.GetSymbol(); + if (token.Value.Length == 0) { + throw new STJsonParseException(m_n_position, "Invalid char '" + ch + "'."); + } + yield return token; + continue; + } + } + this.Dispose(); + } + + public void Dispose() + { + if (this.Disposed) return; + this.Disposed = true; + if (m_is_auto_close) { + m_reader.Dispose(); + } + } + + private char ReadChar() + { + return (char)this.ReadInt(); + } + + private int ReadInt() + { + var ch = m_reader.Read(); + m_n_position++; + m_n_col++; + if (ch == '\n') { + m_n_row++; + m_n_col = 1; + } + return ch; + } + + private STJsonToken GetNumber() + { + StringBuilder sb = new StringBuilder(12); + int n_char = -1; + int n_index = m_n_position, n_row = m_n_row, n_col = m_n_col; + bool b_dot = false, b_e = false, b_hex = false; + while ((n_char = m_reader.Peek()) != -1) { + var ch = (char)n_char; + switch (ch) { + case 'x': // 0x123 0X123 + case 'X': + b_hex = true; + sb.Append(this.ReadChar()); + continue; + case '-': // -123 +123 + case '+': + sb.Append(this.ReadChar()); + continue; + case 'e': // 123E+2 123e2 123e-2 + case 'E': + b_e = true; + sb.Append(this.ReadChar()); + continue; + case '.': // 123.213 .123 123. + b_dot = true; + sb.Append(this.ReadChar()); + continue; + } + if ('0' <= ch && ch <= '9') { + sb.Append(this.ReadChar()); continue; + } + if ('a' <= ch && ch <= 'f') { + sb.Append(this.ReadChar()); continue; + } + if ('A' <= ch && ch <= 'F') { + sb.Append(this.ReadChar()); continue; + } + break; + } + return new STJsonToken() + { + Type = (b_dot || b_e) && !b_hex ? STJsonTokenType.Double : STJsonTokenType.Long, + Index = n_index, + Row = n_row, + Col = n_col, + Value = sb.ToString() + }; + } + + private STJsonToken GetSymbol() + { + StringBuilder sb = new StringBuilder(12); + int n_char = -1; + int n_index = m_n_position, n_row = m_n_row, n_col = m_n_col; + + while ((n_char = m_reader.Peek()) != -1) { + var ch = (char)n_char; + if ('a' <= ch && ch <= 'z') { + sb.Append(this.ReadChar()); continue; + } + if ('A' <= ch && ch <= 'Z') { + sb.Append(this.ReadChar()); continue; + } + if ('0' <= ch && ch <= '9') { + sb.Append(this.ReadChar()); continue; + } + if (ch == '_') { + sb.Append(this.ReadChar()); continue; + } + break; + } + + return new STJsonToken() + { + Type = STJsonTokenType.Symbol, + Index = n_index, + Row = n_row, + Col = n_col, + Value = sb.ToString() + }; + } + + private STJsonToken GetString() + { + StringBuilder sb = new StringBuilder(512); + int n_char = -1; + int n_index = m_n_position, n_row = m_n_row, n_col = m_n_col; + + var ch_begin = this.ReadChar(); + var token = new STJsonToken() + { + Type = STJsonTokenType.String, + Index = n_index, + Row = n_row, + Col = n_col + }; + + while ((n_char = this.ReadInt()) != -1) { + var ch = (char)n_char; + if (ch == ch_begin) { + token.Value = sb.ToString(); + return token; + } + sb.Append(ch); + if (ch == '\\') sb.Append(this.ReadChar()); + } + throw new STJsonParseException(n_index, m_n_row, m_n_col, "Can not get a string. missing '\'' or '\"'."); + } + + private STJsonToken GetComment() + { + // note: str_text[n_index] == '/' + int n_index = m_n_position, n_row = m_n_row, n_col = m_n_col; + var n_char = this.ReadInt(); + if ((n_char = this.ReadInt()) == -1) { + throw new STJsonParseException(n_index, m_n_row, m_n_col, "Invalid char '/'."); + } + switch (n_char) { + case '/': return this.GetCommentLine(); + case '*': return this.GetCommentBlock(); + default: + throw new STJsonParseException(n_index + 1, n_row, n_col + 1, "Invalid char '" + (char)n_char + "'."); + } + } + + private STJsonToken GetCommentLine() + { + // note: str_text[n_index:2] == '//' + int n_char = -1; + + while ((n_char = this.ReadInt()) != -1) { + if (n_char == '\n') { + break; + } + } + return new STJsonToken() + { + Type = STJsonTokenType.Comment + }; + } + + private STJsonToken GetCommentBlock() + { + // note: str_text[n_index:2] == '/*' + int n_char_current = '\0', n_char_last = '\0'; + while ((n_char_current = this.ReadInt()) != -1) { + if (n_char_current == '/' && n_char_last == '*') { + break; + } + n_char_last = n_char_current; + } + return new STJsonToken() + { + Type = STJsonTokenType.Comment + }; + } + + // ============================================= + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + void IDisposable.Dispose() + { + this.Dispose(); + } + } +} diff --git a/Src/STLib.Json/STJson/Parser/STJsonTokenizer.cs b/Src/STLib.Json/STJson/Parser/STJsonTokenizer.cs new file mode 100644 index 0000000..bef53b5 --- /dev/null +++ b/Src/STLib.Json/STJson/Parser/STJsonTokenizer.cs @@ -0,0 +1,258 @@ +using System; +using System.Text; +using System.Collections.Generic; + +namespace STLib.Json +{ + internal class STJsonTokenizer + { + public string Text { get => m_str_json; } + + private string m_str_json; + + private int m_n_position; + private int m_n_row; + private int m_n_col; + + public STJsonTokenizer(string str_json) + { + m_str_json = str_json; + } + + public List GetTokens() + { + m_n_position = 0; + m_n_row = 1; + m_n_col = 1; + + var token = new STJsonToken(); + List lst = new List(); + + while (m_n_position < m_str_json.Length) { + var ch = m_str_json[m_n_position]; + if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+' || ch == '.') { + token = this.GetNumber(); + lst.Add(token); + continue; + } + switch (ch) { + case '{': // object start + //stack_region.Push('{'); + lst.Add(new STJsonToken(m_n_position++, m_n_row, m_n_col++, "{", STJsonTokenType.ObjectStart)); + continue; + case '[': // array start + //stack_region.Push('['); + lst.Add(new STJsonToken(m_n_position++, m_n_row, m_n_col++, "[", STJsonTokenType.ArrayStart)); + continue; + case ',': // item splitor + lst.Add(new STJsonToken(m_n_position++, m_n_row, m_n_col++, ",", STJsonTokenType.ItemSplitor)); + continue; + case ':': // key value splitor + lst.Add(new STJsonToken(m_n_position++, m_n_row, m_n_col++, ":", STJsonTokenType.KVSplitor)); + continue; + case ']': // array end + lst.Add(new STJsonToken(m_n_position++, m_n_row, m_n_col++, "]", STJsonTokenType.ArrayEnd)); + continue; + case '}': // object end + lst.Add(new STJsonToken(m_n_position++, m_n_row, m_n_col++, "}", STJsonTokenType.ObjectEnd)); + continue; + case ' ': // space + case '\r': + case '\n': + case '\t': + m_n_position++; + m_n_col++; + if (ch == '\n') { + m_n_row++; + m_n_col = 1; + } + continue; + case '/': + token = this.GetComment(); + continue; + case '\'': // string + case '\"': + token = this.GetString(); + if (lst.Count > 0 && lst[lst.Count - 1].Type == STJsonTokenType.String) { + var temp = lst[lst.Count - 1]; + temp.Value += token.Value; + lst[lst.Count - 1] = temp; + } else { + lst.Add(token); + } + continue; + default: + token = this.GetSymbol(); + if (token.Value.Length == 0) { + throw new STJsonParseException(m_n_position, "Invalid char '" + ch + "'."); + } + lst.Add(token); + break; + } + } + return lst; + } + + private STJsonToken GetNumber() + { + int n_index = m_n_position, n_len = -1; + bool b_dot = false, b_e = false, b_hex = false; + for (int i = n_index; i < m_str_json.Length; i++) { + var ch = m_str_json[i]; + switch (ch) { + case 'x': // 0x123 0X123 + case 'X': + b_hex = true; + continue; + case '-': // -123 +123 + case '+': + continue; + case 'e': // 123E+2 123e2 123e-2 + case 'E': + b_e = true; + continue; + case '.': // 123.213 .123 123. + b_dot = true; + continue; + } + if ('0' <= ch && ch <= '9') continue; + if ('a' <= ch && ch <= 'f') continue; + if ('A' <= ch && ch <= 'F') continue; + n_len = i - n_index; + break; + } + if (n_len == -1) n_len = m_str_json.Length - n_index; + m_n_position += n_len; + m_n_col += n_len; + return new STJsonToken() + { + Index = n_index, + Type = (b_dot || b_e) && !b_hex ? STJsonTokenType.Double : STJsonTokenType.Long, + Value = m_str_json.Substring(n_index, n_len) + }; + } + + private STJsonToken GetSymbol() + { + int n_col = m_n_col; + int n_index = m_n_position, n_len = -1; + for (var i = n_index; i < m_str_json.Length; i++) { + var ch = m_str_json[i]; + if ('a' <= ch && ch <= 'z') continue; + if ('A' <= ch && ch <= 'Z') continue; + if ('0' <= ch && ch <= '9') continue; + if (ch == '_') continue; + n_len = i - n_index; + break; + } + if (n_len == -1) n_len = m_str_json.Length - n_index; + m_n_position += n_len; + m_n_col += n_len; + return new STJsonToken() + { + Type = STJsonTokenType.Symbol, + Index = n_index, + Row = m_n_row, + Col = n_col, + Value = m_str_json.Substring(n_index, n_len) + }; + } + + private STJsonToken GetString() + { + int n_index = m_n_position; + var ch_begin = m_str_json[n_index]; + var token = new STJsonToken() + { + Index = n_index, + Row = m_n_row, + Col = m_n_col++, + Type = STJsonTokenType.String + }; + for (int i = n_index + 1; i < m_str_json.Length; i++) { + var ch = m_str_json[i]; + m_n_col++; + if (ch == ch_begin) { + token.Value = m_str_json.Substring(n_index + 1, i - n_index - 1); + m_n_position += i - n_index + 1; + return token; + } + switch (ch) { + case '\\': + i++; + continue; + case '\n': + m_n_row++; + m_n_col = 1; + continue; + } + } + throw new STJsonParseException(n_index, m_n_row, m_n_col, "Can not get a string. missing '\'' or '\"'."); + } + + private STJsonToken GetComment() + { + // note: str_text[n_index] == '/' + var n_index = m_n_position + 1; + if (n_index >= m_str_json.Length) { + throw new STJsonParseException(m_n_position, m_n_row, m_n_col, "Invalid char '/'."); + } + var ch = m_str_json[n_index]; + switch (ch) { + case '/': return this.GetCommentLine(); + case '*': return this.GetCommentBlock(); + default: + throw new STJsonParseException(n_index, m_n_row, m_n_col, "Invalid char '" + ch + "'."); + } + } + + private STJsonToken GetCommentLine() + { + // note: str_text[n_index:2] == '//' + int n_index = m_n_position, n_len = -1; + m_n_col += 2; + for (int i = n_index + 2; i < m_str_json.Length; i++) { + m_n_col++; + if (m_str_json[i] == '\n') { + n_len = i - n_index + 1; + m_n_row++; + m_n_col = 1; + break; + } + } + if (n_len == -1) n_len = m_str_json.Length - n_index; + m_n_position += n_len; + return new STJsonToken() + { + Type = STJsonTokenType.Comment + }; + } + + private STJsonToken GetCommentBlock() + { + // note: str_text[n_index:2] == '/*' + m_n_col += 2; + int n_index = m_n_position, n_len = -1; + char ch_current = '\0', ch_last = '\0'; + for (int i = n_index + 2; i < m_str_json.Length; i++) { + ch_current = m_str_json[i]; + m_n_col++; + if (ch_current == '\n') { + m_n_row++; + m_n_col = 1; + } else if (ch_current == '/' && ch_last == '*') { + n_len = i - n_index + 1; + break; + } + ch_last = ch_current; + } + if (n_len == -1) n_len = m_str_json.Length - n_index; + m_n_position += n_len; + return new STJsonToken() + { + Type = STJsonTokenType.Comment + }; + } + } +} + diff --git a/Src/STLib.Json/STJson/Parser_backup/STJsonParserOld.cs b/Src/STLib.Json/STJson/Parser_backup/STJsonParserOld.cs new file mode 100644 index 0000000..630fe0d --- /dev/null +++ b/Src/STLib.Json/STJson/Parser_backup/STJsonParserOld.cs @@ -0,0 +1,229 @@ +//using System; +//using System.Text; +//using System.Collections.Generic; + +//using ME = STLib.Json.STJsonParserOld; + +//namespace STLib.Json +//{ +// internal class STJsonParserOld +// { +// public static STJson Parse(string str_json) +// { +// var tokens = STJsonTokenizerOld.GetTokens(str_json); +// if (tokens.Count == 0) { +// throw new STJsonParseException(0, "Invalid string."); +// } +// int nIndex = 1; +// if (tokens[0].Value == "{") { +// return ME.GetObject(tokens, ref nIndex); +// } + +// if (tokens[0].Value == "[") { +// return ME.GetArray(tokens, ref nIndex); +// } +// throw new STJsonParseException( +// tokens[0].Index, +// "Invalid char form index [" + tokens[0].Index + "]{" + tokens[0].Value + "}" +// ); +// } + +// private static STJson GetObject(List lst_token, ref int n_index) +// { +// STJson json = new STJson(); +// json.SetModel(STJsonValueType.Object); +// if (lst_token[n_index].Value == "}") { +// n_index++; +// return json; +// } +// while (n_index < lst_token.Count) { +// var token = lst_token[n_index++]; +// if (token.Type != STJsonTokenType.String) { +// throw new STJsonParseException( +// token.Index, +// "Invalid char form index [" + token.Index + "]{" + token.Value + "}" +// ); +// } +// var jv = json.SetKey(token.Value); +// token = lst_token[n_index++]; +// if (token.Value != ":") { +// throw new STJsonParseException( +// token.Index, +// "Invalid char form index [" + token.Index + "]{" + token.Value + "}" +// ); +// } +// token = lst_token[n_index++]; +// switch (token.Type) { +// case STJsonTokenType.Keyword: +// switch (token.Value) { +// case "true": +// jv.SetValue(true); +// break; +// case "false": +// jv.SetValue(false); +// break; +// case "null": +// jv.SetValue(value: null); +// break; +// } +// break; +// case STJsonTokenType.String: +// jv.SetValue(ME.ParseString(token)); +// break; +// case STJsonTokenType.Long: +// jv.SetValue(ME.ParseNumberLong(token)); +// break; +// case STJsonTokenType.Double: +// jv.SetValue(ME.ParseNumberDouble(token)); +// break; +// case STJsonTokenType.ObjectStart: +// jv.SetValue(ME.GetObject(lst_token, ref n_index)); +// break; +// case STJsonTokenType.ArrayStart: +// jv.SetValue(ME.GetArray(lst_token, ref n_index)); +// break; +// default: +// throw new STJsonParseException( +// token.Index, +// "Invalid char form index [" + token.Index + "]{" + token.Value + "}" +// ); +// } +// token = lst_token[n_index++]; +// switch (token.Value) { +// case ",": continue; +// case "}": return json; +// default: +// throw new STJsonParseException( +// token.Index, +// "Invalid char form index [" + token.Index + "]{" + token.Value + "}" +// ); +// } +// } +// throw new STJsonParseException(-1, "Incomplete string."); +// } + +// private static STJson GetArray(List lst_token, ref int n_index) +// { +// STJson json = new STJson(); +// json.SetModel(STJsonValueType.Array); +// if (lst_token[n_index].Value == "]") { +// n_index++; +// return json; +// } +// while (n_index < lst_token.Count) { +// var token = lst_token[n_index++]; +// switch (token.Type) { +// case STJsonTokenType.String: +// json.Append(ME.ParseString(token)); +// break; +// case STJsonTokenType.Long: +// json.Append(ME.ParseNumberLong(token)); +// break; +// case STJsonTokenType.Double: +// json.Append(ME.ParseNumberDouble(token)); +// break; +// case STJsonTokenType.Keyword: +// switch (token.Value) { +// case "true": +// json.Append(true); +// break; +// case "false": +// json.Append(false); +// break; +// case "null": +// json.Append(json: null); +// break; +// } +// break; +// case STJsonTokenType.ObjectStart: +// json.Append(ME.GetObject(lst_token, ref n_index)); +// break; +// case STJsonTokenType.ArrayStart: +// json.Append(ME.GetArray(lst_token, ref n_index)); +// break; +// default: +// throw new STJsonParseException( +// token.Index, +// "Invalid char form index [" + token.Index + "]{" + token.Value + "}" +// ); +// } +// token = lst_token[n_index++]; +// switch (token.Value) { +// case ",": continue; +// case "]": return json; +// default: +// throw new STJsonParseException( +// token.Index, +// "Invalid char form index [" + token.Index + "]{" + token.Value + "}" +// ); +// } +// } +// throw new STJsonParseException(-1, "Incomplete string."); +// } + +// private static long ParseNumberLong(STJsonToken token) +// { +// try { +// return Convert.ToInt64(token.Value); +// } catch { +// throw new STJsonParseException(token.Index, "Can not convert [" + token.Value + "] to long."); +// } +// } + +// private static double ParseNumberDouble(STJsonToken token) +// { +// try { +// return Convert.ToDouble(token.Value); +// } catch { +// throw new STJsonParseException(token.Index, "Can not convert [" + token.Value + "] to double."); +// } +// } + +// private static string ParseString(STJsonToken token) +// { +// int n_hex_len = 0; +// string str_temp = string.Empty; +// StringBuilder sb = new StringBuilder(); +// for (int i = 0; i < token.Value.Length; i++) { +// var ch = token.Value[i]; +// if (ch != '\\') { +// sb.Append(ch); +// continue; +// } +// i++; +// if (i >= token.Value.Length) { +// throw new STJsonParseException(token.Index + i, ch); +// } +// ch = token.Value[i]; +// switch (ch) { +// case 'r': sb.Append('\r'); continue; +// case 'n': sb.Append('\n'); continue; +// case 't': sb.Append('\t'); continue; +// case 'f': sb.Append('\f'); continue; +// case 'b': sb.Append('\b'); continue; +// case 'a': sb.Append('\a'); continue; +// case 'v': sb.Append('\v'); continue; +// case '0': sb.Append('\0'); continue; +// case 'x': +// case 'u': +// n_hex_len = ch == 'x' ? 2 : 4; +// if (i + n_hex_len >= token.Value.Length) { +// throw new STJsonParseException(token.Index + i, ch); +// } +// str_temp = token.Value.Substring(i + 1, n_hex_len); +// try { +// sb.Append((char)Convert.ToUInt16(str_temp, 16)); +// } catch { +// throw new STJsonParseException(token.Index + i, "Invalid string [" + (token.Value.Substring(i + str_temp.Length + 2)) + "]"); +// } +// i += n_hex_len; +// continue; +// default: +// sb.Append(ch); +// continue; +// } +// } +// return sb.ToString(); +// } +// } +//} diff --git a/Src/STLib.Json/STJson/Parser_backup/STJsonTokenizerOld.cs b/Src/STLib.Json/STJson/Parser_backup/STJsonTokenizerOld.cs new file mode 100644 index 0000000..aed54fc --- /dev/null +++ b/Src/STLib.Json/STJson/Parser_backup/STJsonTokenizerOld.cs @@ -0,0 +1,201 @@ +//using System; +//using System.Text; +//using System.Collections.Generic; + +//using ME = STLib.Json.STJsonTokenizerOld; + +//namespace STLib.Json +//{ +// internal class STJsonTokenizerOld +// { +// public static List GetTokens(string str_json) +// { +// var token = new STJsonToken(); +// var lst_token = new List(); +// Stack stack_region = new Stack(); +// for (int i = 0; i < str_json.Length; i++) { +// var c = str_json[i]; +// if (('0' <= c && c <= '9') || c == '-') { +// token = ME.GetNumber(str_json, i); +// lst_token.Add(token); +// i += token.Value.Length - 1; +// continue; +// } +// switch (c) { +// case '{': // object start +// stack_region.Push('{'); +// lst_token.Add(new STJsonToken(i, "{", STJsonTokenType.ObjectStart)); +// continue; +// case '[': // array start +// stack_region.Push('['); +// lst_token.Add(new STJsonToken(i, "[", STJsonTokenType.ArrayStart)); +// continue; +// case ',': // item splitor +// lst_token.Add(new STJsonToken(i, ",", STJsonTokenType.ItemSplitor)); +// continue; +// case ':': // key value splitor +// lst_token.Add(new STJsonToken(i, ":", STJsonTokenType.KVSplitor)); +// continue; +// case ']': // array end +// if (stack_region.Count == 0 || stack_region.Pop() != '[') { +// throw new STJsonParseException(i, "Invalid char, missing '[' or '}'. Index: " + i); +// } +// lst_token.Add(new STJsonToken(i, "]", STJsonTokenType.ArrayEnd)); +// continue; +// case '}': // object end +// if (stack_region.Count == 0 || stack_region.Pop() != '{') { +// throw new STJsonParseException(i, "Invalid char, missing '[' or '}'. Index: " + i); +// } +// lst_token.Add(new STJsonToken(i, "}", STJsonTokenType.ObjectEnd)); +// continue; +// case 't': // [keyword] - true +// lst_token.Add(ME.GetKeyword(str_json, i, "true")); +// i += 3; +// continue; +// case 'f': // [keyword] - false +// lst_token.Add(ME.GetKeyword(str_json, i, "false")); +// i += 4; +// continue; +// case 'n': // [keyword] - null +// lst_token.Add(ME.GetKeyword(str_json, i, "null")); +// i += 3; +// continue; +// case '"': // string +// token = ME.GetString(str_json, i); +// i += token.Value.Length + 1; +// lst_token.Add(token); +// continue; +// case ' ': +// case '\r': // space +// case '\n': +// case '\t': +// continue; +// default: +// throw new STJsonParseException(i, "Invalid char. Index: " + i); +// } +// } +// return lst_token; +// } + +// private static STJsonToken GetNumber(string str_text, int n_index) +// { +// bool b_float = false; +// int n_len = -1; +// for (int i = 0; i < str_text.Length; i++) { +// var ch = str_text[i]; +// switch (ch) { +// case '+': +// case '-': +// continue; +// case '.': +// case 'E': +// case 'e': +// b_float = true; +// continue; +// } +// if ('0' <= ch && ch <= '9') continue; +// break; +// } +// return new STJsonToken() +// { +// Index = n_index, +// Type = b_float ? STJsonTokenType.Double : STJsonTokenType.Long, +// Value = n_len == -1 ? str_text.Substring(n_index) : str_text.Substring(n_index, n_len) +// }; +// } + +// /*private static STJsonToken GetNumber(string strText, int nIndex) +// { +// bool bDot = false, bE = false; +// var token = new STJsonToken() +// { +// Index = nIndex, +// Type = STJsonTokenType.Long +// }; +// for (int i = nIndex; i < strText.Length; i++) { +// var c = strText[i]; +// if (c == '-') { // -123 +// if (i != nIndex) { +// throw new STJsonParseException(i, "Invalid char. Index: " + i); +// } +// continue; +// } +// if (c == '.') { // 1.23 +// if (bDot || bE || i == nIndex || strText[i - 1] == '-') { +// throw new STJsonParseException(i, "Invalid char. Index: " + i); +// } +// bDot = true; +// token.Type = STJsonTokenType.Double; +// continue; +// } +// if (c == 'E' || c == 'e') { // 12E+3 Regex:-?\d+(\.\d+)?E[+-]?(\d+) +// if (bE || i == nIndex || strText[i - 1] == '-' || strText[i - 1] == '.') { +// throw new STJsonParseException(i, "Invalid char. Index: " + i); +// } +// if (i + 2 >= strText.Length) { +// throw new STJsonParseException(i, "Invalid char. Index: " + i); +// } +// c = strText[++i]; +// if (c == '-' || c == '+') { // E+ | E- +// c = strText[++i]; +// } +// //if (strText[++i] != '+') { // E+ +// // throw new STJsonParseException(i + 1, "Invalid char. Index: " + (i + 1)); +// //} +// bE = true; +// //c = strText[++i]; +// if (c < '0' || '9' < c) { // E+[0-9] +// throw new STJsonParseException(i + 2, "Invalid char. Index: " + (i + 2)); +// } +// token.Type = STJsonTokenType.Double; +// continue; +// } +// if ('0' <= c && c <= '9') continue; +// token.Value = strText.Substring(nIndex, i - nIndex); +// return token; +// } +// token.Value = strText.Substring(nIndex); +// return token; +// }*/ + +// private static STJsonToken GetKeyword(string str_text, int n_index, string str_keyword) +// { +// var token = new STJsonToken() +// { +// Index = n_index, +// Type = STJsonTokenType.Keyword +// }; +// if (str_text.Substring(n_index, str_keyword.Length) == str_keyword) { +// token.Value = str_keyword; +// return token; +// } +// throw new STJsonParseException(n_index, "Invalid char. Index: " + n_index); +// } + +// private static STJsonToken GetString(string str_text, int n_index) +// { +// var token = new STJsonToken() +// { +// Index = n_index, +// Type = STJsonTokenType.String +// }; +// if (str_text[n_index] != '"') { +// return token; +// } +// for (int i = n_index + 1; i < str_text.Length; i++) { +// var ch = str_text[i]; +// switch (ch) { +// case '\n': +// throw new STJsonParseException(i, "Invalid char(\\n) for a string. Index: " + i); +// case '\\': +// i++; +// continue; +// case '"': +// token.Value = str_text.Substring(n_index + 1, i - n_index - 1); +// return token; +// } +// } +// throw new STJsonParseException(n_index, "Can not get a string. Index: " + n_index); +// } +// } +//} \ No newline at end of file diff --git a/Src/STLib.Json/STJson/STJson.Static.cs b/Src/STLib.Json/STJson/STJson.Static.cs new file mode 100644 index 0000000..a71c74e --- /dev/null +++ b/Src/STLib.Json/STJson/STJson.Static.cs @@ -0,0 +1,453 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace STLib.Json +{ + public partial class STJson + { + internal static List m_lst_key_converter = new List(); + internal static List m_lst_type_converter = new List(); + + internal static Dictionary m_dic_key_converter = new Dictionary(); + internal static Dictionary m_dic_type_converter = new Dictionary(); + + public static void AddCustomConverter(Type type, STJsonConverter converter) + { + int n_code = type.GetHashCode(); + if (m_dic_type_converter.ContainsKey(n_code)) { + m_dic_type_converter[n_code] = converter; + } else { + m_dic_type_converter.Add(n_code, converter); + } + } + + public static void AddCustomConverter(string str_key, STJsonConverter converter) + { + int nCode = str_key.GetHashCode(); + if (m_dic_key_converter.ContainsKey(nCode)) { + m_dic_key_converter[nCode] = converter; + } else { + m_dic_key_converter.Add(nCode, converter); + } + } + + public static void RemoveCustomConverter(Type type) + { + int n_code = type.GetHashCode(); + if (m_dic_type_converter.ContainsKey(n_code)) { + m_dic_type_converter.Remove(n_code); + } + } + + public static void RemoveCustomConverter(string str_key) + { + int n_code = str_key.GetHashCode(); + if (m_dic_key_converter.ContainsKey(n_code)) { + m_dic_key_converter.Remove(n_code); + } + } + + public static STJsonConverter GetConverter(Type type) + { + int n_code = type.GetHashCode(); + if (m_dic_type_converter.ContainsKey(n_code)) { + return m_dic_type_converter[n_code]; + } else { + return STJsonBuildInConverter.Get(n_code); + } + } + + public static STJsonConverter GetCustomConverter(Type type) + { + int n_code = type.GetHashCode(); + if (m_dic_type_converter.ContainsKey(n_code)) { + return m_dic_type_converter[n_code]; + } + return null; + } + + public static STJsonConverter GetCustomConverter(string str_key) + { + int n_code = str_key.GetHashCode(); + if (m_dic_key_converter.ContainsKey(n_code)) { + return m_dic_key_converter[n_code]; + } + return null; + } + + public static string Serialize(object obj) + { + return STJson.Serialize(obj, 0, STJsonSetting.Default); + } + + public static string Serialize(object obj, int n_space_count) + { + return STJson.Serialize(obj, n_space_count, STJsonSetting.Default); + } + + public static string Serialize(object obj, STJsonSetting setting) + { + return STJson.Serialize(obj, 0, setting); + } + + public static string Serialize(object obj, int n_space_count, STJsonSetting setting) + { + StringWriter writer = new StringWriter(); + STJson.Serialize(obj, writer, n_space_count, setting); + return writer.ToString(); + } + + public static void Serialize(object obj, TextWriter writer) + { + STJson.Serialize(obj, writer, 0, STJsonSetting.Default); + } + + public static void Serialize(object obj, TextWriter writer, int n_space_count) + { + STJson.Serialize(obj, writer, n_space_count, STJsonSetting.Default); + } + + public static void Serialize(object obj, TextWriter writer, STJsonSetting setting) + { + STJson.Serialize(obj, writer, 0, setting); + } + + public static void Serialize(object obj, TextWriter writer, int n_space_count, STJsonSetting setting) + { + if (n_space_count < 0) { + n_space_count = 0; + } + ObjectToString.Get(writer, 0, n_space_count, obj, setting); + } + + public static STJson Deserialize(string str_json) + { + return STJsonParser.Parse(str_json); + } + + public static T Deserialize(string str_json) + { + var json = STJsonParser.Parse(str_json); + return STJsonToObject.Get(json, STJsonSetting.Default); + } + + public static T Deserialize(STJson st_json) + { + return STJsonToObject.Get(st_json, STJsonSetting.Default); + } + + public static void Deserialize(string str_json, object obj) + { + var json = STJson.Deserialize(str_json); + STJsonToObject.SetObject(json, obj, STJsonSetting.Default); + } + + public static void Deserialize(STJson st_json, object obj) + { + STJsonToObject.SetObject(st_json, obj, STJsonSetting.Default); + } + + public static T Deserialize(string str_json, STJsonSetting setting) + { + var json = STJson.Deserialize(str_json); + return STJsonToObject.Get(json, setting); + } + + public static T Deserialize(STJson st_json, STJsonSetting setting) + { + return STJsonToObject.Get(st_json, setting); + } + + public static void Deserialize(string st_json, object obj, STJsonSetting setting) + { + var json = STJson.Deserialize(st_json); + STJsonToObject.SetObject(json, obj, setting); + } + + public static void Deserialize(STJson st_json, object obj, STJsonSetting setting) + { + STJsonToObject.SetObject(st_json, obj, setting); + } + + public static STJson New() + { + return new STJson(); + } + + public static STJsonWriter Write(TextWriter writer) + { + return new STJsonWriter(writer, true, 0); + } + + public static STJsonWriter Write(string str_file) + { + return new STJsonWriter(new StreamWriter(str_file, false, Encoding.UTF8), true, 0); + } + + public static STJsonWriter Write(string str_file, int n_space_count) + { + return new STJsonWriter(new StreamWriter(str_file, false, Encoding.UTF8), true, n_space_count); + } + + public static STJsonWriter Write(TextWriter writer, int n_space_count) + { + return new STJsonWriter(writer, true, n_space_count); + } + + public static STJsonWriter Write(TextWriter writer, bool is_auto_close) + { + return new STJsonWriter(writer, is_auto_close, 0); + } + + public static STJsonWriter Write(TextWriter writer, bool is_auto_close, int n_space_count) + { + return new STJsonWriter(writer, is_auto_close, n_space_count); + } + + public static STJsonReader Read(string str_file) + { + return new STJsonReader(new StreamReader(str_file, Encoding.UTF8), true); + } + + public static STJsonReader Read(TextReader reader) + { + return new STJsonReader(reader, true); + } + + public static STJsonReader Read(TextReader reader, bool is_auto_close) + { + return new STJsonReader(reader, is_auto_close); + } + + public static STJson CreateObject() + { + STJson json = new STJson(); + json.SetModel(STJsonValueType.Object); + return json; + } + + public static STJson CreateObject(STJsonWriterStartCallback callback) + { + StringWriter sw = new StringWriter(); + using (var writer = new STJsonWriter(sw)) { + writer.StartWithObject(callback); + } + return STJsonParser.Parse(sw.ToString()); + } + + public static STJson CreateArray(params object[] objs) + { + STJson json = new STJson(); + json.SetModel(STJsonValueType.Array); + foreach (var v in objs) { + json.Append(v); + } + return json; + } + + public static STJson CreateArray(STJsonWriterStartCallback callback) + { + StringWriter sw = new StringWriter(); + using (var writer = new STJsonWriter(sw)) { + writer.StartWithArray(callback); + } + return STJsonParser.Parse(sw.ToString()); + } + + public static STJson FromObject(string value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(int value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(long value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(float value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(double value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(bool value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(DateTime value) + { + STJson json = new STJson(); + json.SetValue(value); + return json; + } + + public static STJson FromObject(object obj) + { + return ObjectToSTJson.Get(obj, STJsonSetting.Default); + } + + public static STJson FromObject(object obj, STJsonSetting setting) + { + if (obj is STJson) return obj as STJson; + return ObjectToSTJson.Get(obj, setting); + } + + //public static string Format(string str_json) + //{ + // return STJson.Format(str_json, 4); + //} + + //public static string Format(string str_json, int n_space_count) + //{ + // var json = STJson.Deserialize(str_json); + // return json.ToString(n_space_count); + //} + + public static string Format(string str_json) + { + return STJson.Format(str_json, 4); + } + + public static string Format(string str_json, int n_space_count) + { + return STJson.Format(str_json, 4, null); + } + + public static string Format(string str_json, int n_space_count, string str_base_space) + { + str_base_space = "\r\n" + str_base_space; + var str_space = string.Empty; + char ch_last = '\0'; + StringBuilder sb = new StringBuilder(); + int n_level = 0; + bool b_is_string = false; + //bool b_is_array = false; + //Stack stack_is_array = new Stack(); + //foreach (var c in str_json) { + for (int i = 0; i < str_json.Length; i++) { + var ch = str_json[i]; + if (ch == '"' && ch_last != '\\') { + b_is_string = !b_is_string; + } + if (!b_is_string) { + switch (ch) { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + case ':': + sb.Append(": "); + continue; + case ',': + //if (b_is_array) { + // sb.Append(", "); + //} else { + sb.Append("," + str_base_space); + sb.Append(/*"".PadRight(n_level * n_space_count)*/str_space); + //} + continue; + case '{': + case '[': + if (i + 1 < str_json.Length) { + switch (str_json[i + 1]) { + case '}': + case ']': + sb.Append(ch); + sb.Append(str_json[++i]); + continue; + } + } + //b_is_array = ch == '['; + //stack_is_array.Push(b_is_array); + n_level++; + str_space = "".PadRight(n_level * n_space_count); + sb.Append(ch); + sb.Append(str_base_space); + sb.Append(/*"".PadRight(n_level * n_space_count)*/str_space); + continue; + case '}': + case ']': + //if (stack_is_array.Count == 0) { + // b_is_array = false; + //} else { + // stack_is_array.Pop(); + // b_is_array = stack_is_array.Count == 0 ? false : stack_is_array.First(); + //} + n_level--; + str_space = "".PadRight(n_level * n_space_count); + sb.Append(str_base_space); + sb.Append(/*"".PadRight(n_level * n_space_count)*/str_space); + sb.Append(ch); + continue; + } + } + sb.Append(ch); + ch_last = ch; + } + return sb.ToString(); + } + + internal static string Escape(string str_text) + { + if (string.IsNullOrEmpty(str_text)) return str_text; + StringBuilder sb = new StringBuilder(str_text.Length); + for (int i = 0; i < str_text.Length; i++) { + var ch = str_text[i]; + if (0x5D <= ch && ch <= 0x10FFFF) { + sb.Append(ch); + continue; + } + if (0x23 <= ch && ch <= 0x5B) { + sb.Append(ch); + continue; + } + if (0x20 <= ch && ch <= 0x21) { + sb.Append(ch); + continue; + } + switch (str_text[i]) { + case '\"': sb.Append("\\\""); continue; + case '\\': sb.Append("\\\\"); continue; + case '\b': sb.Append("\\b"); continue; + case '\f': sb.Append("\\f"); continue; + case '\n': sb.Append("\\n"); continue; + case '\r': sb.Append("\\r"); continue; + case '\t': sb.Append("\\t"); continue; + default: + sb.Append("\\u" + ((int)ch).ToString("X4")); + continue; + } + } + if (sb.Length == str_text.Length) { + return str_text; + } + return sb.ToString(); + } + } +} + diff --git a/Src/STLib.Json/STJson/STJson.cs b/Src/STLib.Json/STJson/STJson.cs new file mode 100644 index 0000000..0f5136c --- /dev/null +++ b/Src/STLib.Json/STJson/STJson.cs @@ -0,0 +1,754 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace STLib.Json +{ + public partial class STJson : IEnumerable, IEnumerable, ICloneable + { + private STJson m_ref_json; + + public string Key { get; private set; } + public object Value { get; private set; } + public STJsonValueType ValueType { get; internal set; } + + [Obsolete("STJson.IsNullObject will be removed in the next version. Please use STJson.IsNullValue.")] + public bool IsNullObject + { + get { + //if (this.Value == null) { + // return m_dic_values == null && m_lst_values == null; + //} + //return false; + return this.IsNullValue; + } + } + + public bool IsNullValue + { + get { + return /*this.Value == null &&*/ this.ValueType == STJsonValueType.Undefined; + } + } + + public bool IsNumber + { + get { return this.ValueType == STJsonValueType.Long || this.ValueType == STJsonValueType.Double; } + } + + public int Count + { + get { + switch (this.ValueType) { + case STJsonValueType.Array: + return m_lst_values.Count; + case STJsonValueType.Object: + return m_dic_values.Count; + } + return 0; + } + } + + public STJson this[string key] + { + get { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Object."); + } + if (m_dic_values.ContainsKey(key)) { + return m_dic_values[key]; + } + return null; + } + set { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Object."); + } + if (m_dic_values.ContainsKey(key)) { + m_dic_values[key].SetValue(value); + } else { + m_dic_values.Add(key, value); + } + } + } + + public STJson this[int index] + { + get { + if (this.ValueType != STJsonValueType.Array) { + throw new STJsonException("Current STJson is not Array."); + } + return m_lst_values[index]; + } + set { + if (this.ValueType != STJsonValueType.Array) { + throw new STJsonException("Current STJson is not Array."); + } + m_lst_values[index] = value; + } + } + + private List m_lst_values; + private Dictionary m_dic_values; + + public STJson() + { + this.ValueType = STJsonValueType.Undefined; + m_lst_values = new List(); + m_dic_values = new Dictionary(); + } + + private STJson(string key) : this() + { + if (key == null) { + key = "null"; + } + this.Key = key; + } + + public string[] GetKeys() + { + if (this.ValueType != STJsonValueType.Object) { + return null; + } + int i = 0; + string[] strs = new string[m_dic_values.Count]; + foreach (var v in m_dic_values.Keys) { + strs[i++] = v; + } + return strs; + } + + public STJson SetKey(string str_key) + { + this.SetModel(STJsonValueType.Object); + if (str_key == null) { + str_key = "null"; + } + if (!m_dic_values.ContainsKey(str_key)) { + m_dic_values.Add(str_key, new STJson(str_key)); + } + return m_dic_values[str_key]; + } + + public STJson Remove(string str_key) + { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Object."); + } + if (str_key == null) { + str_key = "null"; + } + STJson json = null; + if (m_dic_values.ContainsKey(str_key)) { + json = m_dic_values[str_key]; + m_dic_values.Remove(str_key); + } + return json; + } + + public STJson Remove(int n_index) + { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Array."); + } + var json = m_lst_values[n_index]; + m_lst_values.RemoveAt(n_index); + return json; + } + + [Obsolete("STJson.Delete(string) will be removed in the next version. Please use STJson.Remove(string).")] + public STJson Delete(string key) + { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Object."); + } + if (key == null) { + key = "null"; + } + STJson json = null; + if (m_dic_values.ContainsKey(key)) { + json = m_dic_values[key]; + m_dic_values.Remove(key); + } + return json; + } + + [Obsolete("STJson.Delete(int) will be removed in the next version. Please use STJson.Remove(int).")] + public STJson RemoveAt(int n_index) + { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Array."); + } + var json = m_lst_values[n_index]; + m_lst_values.RemoveAt(n_index); + return json; + } + + public void Clear() + { + //if (m_dic_values != null) { + m_dic_values.Clear(); + //} + //if (m_lst_values != null) { + m_lst_values.Clear(); + //} + this.Value = null; + } + + #region SetItem + + public STJson SetItem(string key, string value) + { + STJson json = this.SetKey(key); + json.SetValue(value); + return this; + } + + public STJson SetItem(string key, int value) + { + STJson json = this.SetKey(key); + json.SetValue(value); + return this; + } + + public STJson SetItem(string key, long value) + { + STJson json = this.SetKey(key); + json.SetValue(value); + return this; + } + + public STJson SetItem(string key, double value) + { + STJson json = this.SetKey(key); + json.SetValue(value); + return this; + } + + public STJson SetItem(string key, bool value) + { + STJson json = this.SetKey(key); + json.SetValue(value); + return this; + } + + public STJson SetItem(string key, DateTime value) + { + STJson json = this.SetKey(key); + json.SetValue(value); + return this; + } + + public STJson SetItem(string key, STJson json) + { + this.SetKey(key);//.SetValue(json); + m_dic_values[key] = json; + return this; + } + + public STJson SetItem(string key, object obj) + { + STJson json = this.SetKey(key); + json.SetValue(obj); + return this; + } + + #endregion SetItem + + #region SetValue + + public void SetValue(string value) + { + if (value == null) { + this.SetValue(json: null); + return; + } + this.Value = value; + this.SetModel(STJsonValueType.String); + } + + public void SetValue(int value) + { + this.Value = value; + this.SetModel(STJsonValueType.Long); + } + + public void SetValue(long value) + { + this.Value = value; + this.SetModel(STJsonValueType.Long); + } + + public void SetValue(float value) + { + this.Value = value; + this.SetModel(STJsonValueType.Double); + } + + public void SetValue(double value) + { + this.Value = value; + this.SetModel(STJsonValueType.Double); + } + + public void SetValue(bool value) + { + this.Value = value; + this.SetModel(STJsonValueType.Boolean); + } + + public void SetValue(DateTime value) + { + this.Value = value; + this.SetModel(STJsonValueType.Datetime); + } + + public void SetValue(STJson json) + { + this.SetValue(json, true); + } + + private void SetValue(STJson json, bool is_make_ref) + { + if (json == null) { + if (m_ref_json != null) { + m_ref_json.m_ref_json = null; + m_ref_json = null; + } + this.Value = null; + this.SetModel(STJsonValueType.Undefined); + //this.SetModel(STJsonValueType.Object); + return; + } + this.Value = json.Value; + this.ValueType = json.ValueType; + m_dic_values = json.m_dic_values; + m_lst_values = json.m_lst_values; + if (is_make_ref) { + json.m_ref_json = this; + m_ref_json = json; + } + } + + public void SetValue(object obj) + { + if (obj is STJson) { + this.SetValue((STJson)obj, true); + } else { + this.SetValue(STJson.FromObject(obj), false); + } + } + + #endregion SetValue + + #region GetValue + + public string GetValue() + { + if (this.Value == null) { + return null; + } + return this.Value.ToString(); + } + + public T GetValue() + { + if (this.Value == null) { + return default(T); + } + var type = typeof(T); + bool b_processed = true; + var convert = STJsonBuildInConverter.Get(type); + if (convert != null) { + var value = convert.JsonToObject(type, this, ref b_processed); + if (b_processed) { + return (T)value; + } + } + return (T)this.Value; + } + + public T GetValue(T default_value) + { + try { + return this.GetValue(); + } catch { + return default_value; + } + } + + public bool GetValue(out T result) + { + try { + result = this.GetValue(); + return true; + } catch { + result = default(T); + return false; + } + } + + #endregion GetValue + + + #region Append + + public STJson Append(string value) + { + return this.Append(STJson.FromObject(value)); + } + + public STJson Append(int value) + { + return this.Append(STJson.FromObject(value)); + } + + public STJson Append(long value) + { + return this.Append(STJson.FromObject(value)); + } + + public STJson Append(double value) + { + return this.Append(STJson.FromObject(value)); + } + + public STJson Append(bool value) + { + return this.Append(STJson.FromObject(value)); + } + + public STJson Append(DateTime value) + { + return this.Append(STJson.FromObject(value)); + } + + public STJson Append(params object[] objs) + { + this.SetModel(STJsonValueType.Array); + foreach (var v in objs) { + this.Append(STJson.FromObject(v)); + } + return this; + } + + public STJson Append(STJson json) + { + m_lst_values.Add(json); + this.SetModel(STJsonValueType.Array); + return this; + } + + #endregion Append + + #region Insert + + public STJson Insert(int n_index, string value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, int value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, long value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, double value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, bool value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, DateTime value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, object value) + { + return this.Insert(n_index, STJson.FromObject(value)); + } + + public STJson Insert(int n_index, STJson json) + { + if (this.ValueType != STJsonValueType.Object) { + throw new STJsonException("Current STJson is not Array."); + } + m_lst_values.Insert(n_index, json); + return this; + } + + #endregion Insert + + public override string ToString() + { + return this.ToString(0); + } + + public string ToString(int n_space_count) + { + StringWriter writer = new StringWriter(); + this.ToString(writer, n_space_count); + return writer.ToString(); + } + + public void ToString(TextWriter writer, int n_space_count) + { + if (n_space_count < 0) { + n_space_count = 0; + } + this.ToString(writer, 0, n_space_count); + } + + internal void ToString(TextWriter writer, int n_level, int n_space_count) + { + var str_space_base = "".PadLeft((n_level) * n_space_count); + var str_space_inc = "".PadLeft((n_level + 1) * n_space_count); + int n_index = 0, n_len = 0; + switch (this.ValueType) { + case STJsonValueType.Undefined: + writer.Write("null"); + break; + case STJsonValueType.Long: + case STJsonValueType.Double: + writer.Write(this.Value.ToString()); + break; + case STJsonValueType.String: + if (this.Value == null) { + writer.Write("null"); + } else { + writer.Write('\"'); + writer.Write(STJson.Escape(this.Value.ToString())); + writer.Write('\"'); + } + break; + case STJsonValueType.Boolean: + writer.Write(true.Equals(this.Value) ? "true" : "false"); + break; + case STJsonValueType.Datetime: + writer.Write('\"'); + writer.Write(((DateTime)this.Value).ToString("O")); + writer.Write('\"'); + break; + case STJsonValueType.Array: + //if (m_lst_values == null) { + // writer.Write("null"); + // break; + //} + if (m_lst_values.Count == 0) { + writer.Write("[]"); + break; + } + writer.Write('['); + for (n_index = 0, n_len = m_lst_values.Count; n_index < n_len; n_index++) { + if (n_space_count != 0) { + writer.Write("\r\n" + str_space_inc); + } + if (m_lst_values[n_index] == null) { + writer.Write("null"); + } else { + m_lst_values[n_index].ToString(writer, n_level + 1, n_space_count); + } + if (n_index != n_len - 1) { + writer.Write(','); + } + } + writer.Write(n_space_count == 0 ? "]" : ("\r\n" + str_space_base + "]")); + break; + case STJsonValueType.Object: + //if (m_dic_values == null) { + // writer.Write("null"); + // break; + //} + writer.Write('{'); + if (m_dic_values.Count == 0) { + writer.Write("{}"); + break; + } + n_index = 0; n_len = m_dic_values.Count; + foreach (var v in m_dic_values) { + writer.Write(n_space_count == 0 ? "\"" : ("\r\n" + str_space_inc + "\"")); + writer.Write(STJson.Escape(v.Key.ToString())); + writer.Write(n_space_count == 0 ? "\":" : "\": "); + v.Value.ToString(writer, n_level + 1, n_space_count); + if (++n_index != n_len) { + writer.Write(','); + } + } + writer.Write(n_space_count == 0 ? "}" : ("\r\n" + str_space_base + "}")); + break; + default: + writer.Write("{ERROR}"); + break; + } + } + + //private void ToStringPrivate(StringBuilder sb) + //{ + // switch (this.ValueType) { + // case STJsonValueType.Long: + // case STJsonValueType.Double: + // sb.Append(this.Value.ToString()); + // break; + // case STJsonValueType.String: + // if (this.Value == null) { + // sb.Append("null"); + // } else { + // sb.Append('\"'); + // sb.Append(STJson.Escape(this.Value.ToString())); + // sb.Append('\"'); + // } + // break; + // case STJsonValueType.Boolean: + // sb.Append(true.Equals(this.Value) ? "true" : "false"); + // break; + // case STJsonValueType.Array: + // if (m_lst_values == null) { + // sb.Append("null"); + // break; + // } + // sb.Append('['); + // foreach (var v in m_lst_values) { + // if (v == null) { + // sb.Append("null"); + // } else { + // v.ToStringPrivate(sb); + // } + // sb.Append(','); + // } + // if (m_lst_values.Count == 0) { + // sb.Append(']'); + // } else { + // sb[sb.Length - 1] = ']'; + // } + // break; + // case STJsonValueType.Datetime: + // sb.Append('\"'); + // sb.Append(((DateTime)this.Value).ToString("O")); + // sb.Append('\"'); + // break; + // case STJsonValueType.Object: + // if (m_dic_values == null) { + // sb.Append("null"); + // break; + // } + // sb.Append('{'); + // foreach (var v in m_dic_values) { + // sb.Append('\"'); + // sb.Append(STJson.Escape(v.Key.ToString())); + // sb.Append("\":"); + // v.Value.ToStringPrivate(sb); + // sb.Append(','); + // } + // if (m_dic_values.Count == 0) { + // sb.Append('}'); + // } else { + // sb[sb.Length - 1] = '}'; + // } + // break; + // default: + // sb.Append("{}"); + // break; + // } + //} + + public IEnumerator GetEnumerator() + { + switch (this.ValueType) { + case STJsonValueType.Array: + foreach (var v in m_lst_values) { + yield return v; + } + break; + case STJsonValueType.Object: + foreach (var v in m_dic_values) { + yield return v.Value; + } + break; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public STJson Clone() + { + STJson json = new STJson(); + json.Value = this.Value; + json.ValueType = this.ValueType; + if (/*m_dic_values != null && */m_dic_values.Count > 0) { + //if (json.m_dic_values == null) { + // json.m_dic_values = new Dictionary(); + //} + foreach (var v in m_dic_values) { + json.m_dic_values.Add(v.Key, v.Value.Clone()); + } + } + if (/*m_lst_values != null && */m_lst_values.Count > 0) { + //if (json.m_lst_values == null) { + // json.m_lst_values = new List(); + //} + foreach (var v in m_lst_values) { + json.m_lst_values.Add(v.Clone()); + } + } + return json; + } + + object ICloneable.Clone() + { + return this.Clone(); + } + + internal void SetModel(STJsonValueType value_type) + { + if (this.ValueType == value_type) return; + this.ValueType = value_type; + switch (value_type) { + case STJsonValueType.Long: + case STJsonValueType.Double: + case STJsonValueType.String: + case STJsonValueType.Boolean: + case STJsonValueType.Datetime: + m_dic_values.Clear();// = null; + m_lst_values.Clear();// = null; + break; + case STJsonValueType.Array: + m_dic_values.Clear();// = null; + //if (m_lst_values == null) { + // m_lst_values = new List(); + //} + break; + case STJsonValueType.Object: + m_lst_values.Clear();// = null; + //if (m_dic_values == null) { + // m_dic_values = new Dictionary(); + //} + break; + default:// case STJsonValueType.Undefined: + this.Value = null; + m_dic_values.Clear();// = null; + m_lst_values.Clear();// = null; + break; + } + if (m_ref_json != null) { + m_ref_json.Value = this.Value; + m_ref_json.ValueType = this.ValueType; + } + } + } +} + diff --git a/Src/STLib.Json/STJson/STJsonReader.cs b/Src/STLib.Json/STJson/STJsonReader.cs new file mode 100644 index 0000000..ca7dc4c --- /dev/null +++ b/Src/STLib.Json/STJson/STJsonReader.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.IO; +using System.Collections; + +namespace STLib.Json +{ + public class STJsonReader : IEnumerable, IDisposable + { + private class StackInfo + { + public char Symbol { get; set; } + public int Level { get; set; } + public string Key { get; set; } + public int Index { get; set; } + + public StackInfo(char ch_symbol, int n_level) + { + this.Symbol = ch_symbol; + this.Level = n_level; + } + } + + public bool Disposed { get; private set; } + public bool IsAutoClose { get; private set; } + + internal STJsonTokenReader TokenReader { get { return m_token_reader; } } + + private bool m_b_started; + private STJsonTokenReader m_token_reader; + private Stack m_stack_path = new Stack(); + + private StackInfo m_current_stack = null; + private List m_lst_stack_info = new List(16); + + public STJsonReader(string str_file) : this(new StreamReader(str_file, Encoding.UTF8), true) { } + + public STJsonReader(TextReader reader) : this(reader, true) { } + + public STJsonReader(TextReader reader, bool is_auto_close) + { + this.IsAutoClose = is_auto_close; + m_token_reader = new STJsonTokenReader(reader, is_auto_close); + } + + public IEnumerator GetEnumerator() + { + STJsonReaderItem item = null; + while ((item = this.GetNextItem()) != null) { + yield return item; + } + this.Dispose(); + } + + public STJsonReaderItem GetNextItem() + { + this.CheckStarted(); + STJsonReaderItem item = null; + if (m_current_stack != null) { + switch (m_current_stack.Symbol) { + case '{': // '{' + item = this.GetNextObjectKV(); + break; + default: // '[' + item = this.GetNextArrayItem(); + break; + } + } + if (item == null) { + this.Dispose(); + } + return item; + } + + public void Dispose() + { + if (this.Disposed) { + return; + } + this.Disposed = true; + m_current_stack = null; + m_stack_path.Clear(); + m_lst_stack_info.Clear(); + m_token_reader.Dispose(); + } + + // ===================================== + + internal void PushStack(char ch_symbol, int n_level) + { + m_current_stack = new StackInfo(ch_symbol, n_level); + m_lst_stack_info.Add(m_current_stack); + } + + internal void PopStack() + { + m_current_stack = null; + if (m_lst_stack_info.Count == 0) return; + m_lst_stack_info.RemoveAt(m_lst_stack_info.Count - 1); + if (m_lst_stack_info.Count == 0) return; + m_current_stack = m_lst_stack_info[m_lst_stack_info.Count - 1]; + } + + // ===================================== + + private void CheckStarted() + { + if (!m_b_started) { + m_b_started = true; + var token = this.GetNextFilteredToken(); + if (token.Type == STJsonTokenType.None) { + return; + } + switch (token.Type) { + case STJsonTokenType.ObjectStart: + case STJsonTokenType.ArrayStart: + break; + default: + throw new STJsonParseException(token); + } + this.PushStack(token.Value[0], 0); + } + } + + private STJsonToken GetNextFilteredToken() + { + foreach (var v in m_token_reader) { + switch (v.Type) { + case STJsonTokenType.KVSplitor: // : + case STJsonTokenType.ItemSplitor: // , + continue; + default: + return v; + } + } + return STJsonToken.None; + } + + private STJsonReaderItem GetNextObjectKV() + { + var token_key = this.GetNextFilteredToken(); + switch (token_key.Type) { + case STJsonTokenType.None: + return null; + case STJsonTokenType.ObjectEnd: + this.PopStack(); + return this.GetNextItem(); + case STJsonTokenType.Symbol: + case STJsonTokenType.String: + break; + default: + throw new STJsonParseException(token_key); + } + m_current_stack.Key = token_key.Value; + var token_val = this.GetNextFilteredToken(); + if (token_val.Type == STJsonTokenType.None) { + return null; + //throw new Exception("error"); + } + var item = new STJsonReaderItem(this, token_val) + { + ParentType = STJsonValueType.Object, + Key = token_key.Value, + Text = token_val.Value + }; + return this.CheckValueToken(item, token_val); + } + + private STJsonReaderItem GetNextArrayItem() + { + var token = this.GetNextFilteredToken(); + switch (token.Type) { + case STJsonTokenType.None: + return null; + case STJsonTokenType.ArrayEnd: + this.PopStack(); + return this.GetNextItem(); + } + var item = new STJsonReaderItem(this, token) + { + ParentType = STJsonValueType.Array, + Index = m_current_stack.Index++, + Text = token.Value + }; + return this.CheckValueToken(item, token); + } + + private STJsonReaderItem CheckValueToken(STJsonReaderItem item, STJsonToken token) + { + this.SetItemPath(item); + switch (token.Type) { + case STJsonTokenType.ObjectStart: + item.ValueType = STJsonValueType.Object; + item.Text = "{...}"; + this.PushStack('{', m_current_stack.Level + 1); + return item; + case STJsonTokenType.ArrayStart: + item.ValueType = STJsonValueType.Array; + item.Text = "[...]"; + this.PushStack('[', m_current_stack.Level + 1); + return item; + case STJsonTokenType.Double: + item.ValueType = STJsonValueType.Double; + return item; + case STJsonTokenType.Long: + item.ValueType = STJsonValueType.Long; + return item; + case STJsonTokenType.String: + item.ValueType = STJsonValueType.String; + return item; + case STJsonTokenType.Symbol: + switch (token.Value) { + case "true": + case "false": + item.ValueType = STJsonValueType.Boolean; + return item; + case "null": + case "undefined": + item.ValueType = STJsonValueType.Object; + return item; + } + break; + } + throw new STJsonParseException(token); + } + + private void SetItemPath(STJsonReaderItem item) + { + int n_index = 0; + var str_path = string.Empty; + object[] obj_path = new object[m_lst_stack_info.Count]; + foreach (var v in m_lst_stack_info) { + switch (v.Symbol) { + case '{': + if (n_index != 0) str_path += '.'; + obj_path[n_index++] = v.Key; + if (v.Key.IndexOf('.') != -1) { + str_path += "'" + v.Key.Replace("'", "\\'") + "'"; + } else { + str_path += v.Key; + } + break; + case '[': + obj_path[n_index++] = v.Index - 1; // m_current_index++; + str_path += "[" + (v.Index - 1) + ']'; + break; + } + } + item.Path = str_path; + item.PathArray = obj_path; + } + + // =================================================== + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + void IDisposable.Dispose() + { + this.Dispose(); + } + } +} diff --git a/Src/STLib.Json/STJson/STJsonReaderItem.cs b/Src/STLib.Json/STJson/STJsonReaderItem.cs new file mode 100644 index 0000000..55f4b43 --- /dev/null +++ b/Src/STLib.Json/STJson/STJsonReaderItem.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace STLib.Json +{ + public class STJsonReaderItem + { + public string Path { get; internal set; } + public object PathArray { get; internal set; } + public string Key { get; internal set; } + public int Index { get; internal set; } + public string Text { get; internal set; } + public STJsonValueType ParentType { get; internal set; } + public STJsonValueType ValueType { get; internal set; } + + private STJsonReader m_reader; + private STJsonToken m_value_token; + + internal STJsonReaderItem(STJsonReader reader, STJsonToken token) + { + m_reader = reader; + m_value_token = token; + } + + public STJson GetSTJson() + { + switch (this.ValueType) { + case STJsonValueType.String: + return STJson.FromObject(this.Text); + case STJsonValueType.Double: + return STJson.FromObject(double.Parse(this.Text)); + case STJsonValueType.Long: + return STJson.FromObject(long.Parse(this.Text)); + case STJsonValueType.Boolean: + return STJson.FromObject(bool.Parse(this.Text)); + case STJsonValueType.Array: + return this.GetArray(); + case STJsonValueType.Object: + return this.GetObject(); + } + return new STJson(); + } + + private STJson GetObject() + { + int n_counter = 1; + List lst_token = new List() { m_value_token }; + foreach (var token in m_reader.TokenReader) { + lst_token.Add(token); + switch (token.Type) { + case STJsonTokenType.ObjectStart: + n_counter++; + continue; + case STJsonTokenType.ObjectEnd: + if (--n_counter == 0) { + break; + } + continue; + default: continue; + } + break; + } + m_reader.PopStack(); + return STJsonParser.Parse(lst_token); + } + + private STJson GetArray() + { + int n_counter = 1; + List lst_token = new List() { m_value_token }; + foreach (var token in m_reader.TokenReader) { + lst_token.Add(token); + switch (token.Type) { + case STJsonTokenType.ArrayStart: + n_counter++; + continue; + case STJsonTokenType.ArrayEnd: + if (--n_counter == 0) { + break; + } + continue; + default: continue; + } + break; + } + m_reader.PopStack(); + return STJsonParser.Parse(lst_token); + } + } +} diff --git a/Src/STLib.Json/STJsonSetting.cs b/Src/STLib.Json/STJson/STJsonSetting.cs similarity index 86% rename from Src/STLib.Json/STJsonSetting.cs rename to Src/STLib.Json/STJson/STJsonSetting.cs index 082d79a..22f6da7 100644 --- a/Src/STLib.Json/STJsonSetting.cs +++ b/Src/STLib.Json/STJson/STJsonSetting.cs @@ -14,10 +14,11 @@ public enum KeyMode public bool EnumUseNumber { get; set; } public bool IgnoreAttribute { get; set; } public bool IgnoreNullValue { get; set; } - public KeyMode KyeMode { get; set; } + public KeyMode Mode { get; set; } public HashSet KeyList { get; private set; } - public STJsonSetting() { + public STJsonSetting() + { this.KeyList = new HashSet(); } } diff --git a/Src/STLib.Json/STJson/STJsonWriter.cs b/Src/STLib.Json/STJson/STJsonWriter.cs new file mode 100644 index 0000000..2e90f94 --- /dev/null +++ b/Src/STLib.Json/STJson/STJsonWriter.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.IO; + +namespace STLib.Json +{ + public delegate void STJsonWriterCallback(); + public delegate void STJsonWriterStartCallback(STJsonWriter writer); + + public class STJsonWriter : IDisposable + { + private enum LevelType { None, Object, Array } + private class StackInfo + { + public int ItemCount { get; set; } + public int Level { get; private set; } + public LevelType LeveType { get; private set; } + public string Space { get; private set; } + + public StackInfo(int n_level, LevelType type, string str_space) + { + this.Level = n_level; + this.LeveType = type; + this.Space = str_space; + } + } + + //==================================================== + + private bool m_b_start; + private Stack m_stack = new Stack(); + private StackInfo m_current_stack = null; + + private TextWriter m_writer; + + public bool Disposed { get; private set; } + public bool IsAutoClose { get; private set; } + public int SpaceCount { get; private set; } + + public int Level + { + get { + return m_current_stack == null ? 0 : m_current_stack.Level; + } + } + + public STJsonValueType ValueType + { + get { + if (m_current_stack == null) return STJsonValueType.Undefined; + switch (m_current_stack.LeveType) { + case LevelType.Array: + return STJsonValueType.Array; + case LevelType.Object: + return STJsonValueType.Object; + } + return STJsonValueType.Undefined; + } + } + + public STJsonWriter(string str_file) : this(new StreamWriter(str_file, false, Encoding.UTF8), true, 0) { } + + public STJsonWriter(string str_file, int n_space_count) : this(new StreamWriter(str_file, false, Encoding.UTF8), true, n_space_count) { } + + public STJsonWriter(TextWriter writer) : this(writer, true, 0) { } + + public STJsonWriter(TextWriter writer, int n_space_count) : this(writer, true, n_space_count) { } + + public STJsonWriter(TextWriter writer, bool is_auto_close, int n_space_count) + { + m_writer = writer; + this.IsAutoClose = is_auto_close; + this.SpaceCount = n_space_count; + if (this.SpaceCount < 0) { + this.SpaceCount = 0; + } + this.PushStack(0, LevelType.None); + } + + public void StartWithObject(STJsonWriterStartCallback callback) + { + if (m_b_start) throw new STJsonWriterException("It's already started."); + m_b_start = true; + this.CheckDisposedOrStarted(); + this.CreateObject(callback, null); + } + + public void StartWithArray(STJsonWriterStartCallback callback) + { + if (m_b_start) throw new STJsonWriterException("It's already started."); + m_b_start = true; + this.CheckDisposedOrStarted(); + this.CreateArray(callback, null); + } + + public STJsonWriter Append(object value) + { + this.CheckDisposedOrStarted(); + if (m_current_stack.LeveType != LevelType.Array) { // check current level + throw new STJsonWriterException("Current level is not a array. The current operation is only available at the array level."); + } + if (m_current_stack.ItemCount > 0) { // not the first item need add (,) + m_writer.Write(','); + } + if (this.SpaceCount != 0) { + m_writer.Write("\r\n" + m_current_stack.Space); + ObjectToString.Get(m_writer, m_current_stack.Level, this.SpaceCount, value, STJsonSetting.Default); + } else { + m_writer.Write(STJson.Serialize(value)); + } + m_current_stack.ItemCount++; + return this; + } + + public STJsonWriter SetItem(string str_key, object value) + { + this.CheckDisposedOrStarted(); + if (m_current_stack.LeveType != LevelType.Object) { + throw new STJsonWriterException("Current level is not a object. The current operation is only available at the object level."); + } + if (m_current_stack.ItemCount++ > 0) { + m_writer.Write(','); + } + var str_text = "\"" + STJson.Escape(str_key) + "\":"; + if (this.SpaceCount != 0) { + m_writer.Write("\r\n" + m_current_stack.Space + str_text + " "); + ObjectToString.Get(m_writer, m_current_stack.Level, this.SpaceCount, value, STJsonSetting.Default); + } else { + m_writer.Write(str_text + STJson.Serialize(value)); + } + return this; + } + + public STJsonWriter SetObject(string str_key, STJsonWriterCallback callback) + { + return this.SetObject(str_key, true, callback); + } + + public STJsonWriter SetObject(string str_key, bool b_express, STJsonWriterCallback callback) + { + if (!b_express) return this; + this.CheckDisposedOrStarted(); + this.SetKey(str_key).CreateObject(null, callback); + return this; + } + + public STJsonWriter SetArray(string str_key, STJsonWriterCallback callback) + { + return this.SetArray(str_key, true, callback); + } + + public STJsonWriter SetArray(string str_key, bool b_express, STJsonWriterCallback callback) + { + if (!b_express) return this; + this.CheckDisposedOrStarted(); + this.SetKey(str_key).CreateArray(null, callback); + return this; + } + + public STJsonWriter CreateObject(STJsonWriterCallback callback) + { + this.CheckDisposedOrStarted(); + if (m_current_stack.LeveType != LevelType.Array) { + throw new STJsonWriterException("Current level is not a array. The current operation is only available at the array level."); + } + if (m_current_stack.ItemCount++ > 0) { + m_writer.Write(','); + } + if (this.SpaceCount != 0) { + m_writer.Write("\r\n" + m_current_stack.Space); + } + + return this.CreateObject(null, callback); + } + + public STJsonWriter CreateArray(STJsonWriterCallback callback) + { + this.CheckDisposedOrStarted(); + if (m_current_stack.LeveType != LevelType.Array) { + throw new STJsonWriterException("Current level is not a array. The current operation is only available at the array level."); + } + if (m_current_stack.ItemCount++ > 0) { + m_writer.Write(','); + } + if (this.SpaceCount != 0) { + m_writer.Write("\r\n" + m_current_stack.Space); + } + + return this.CreateArray(null, callback); + } + + public void Dispose() + { + if (this.Disposed) return; + this.Disposed = true; + m_stack.Clear(); + //if (m_writer == null) return; + m_writer.Flush(); + if (this.IsAutoClose) m_writer.Dispose(); + } + + void IDisposable.Dispose() + { + this.Dispose(); + } + + //================================== + + private void CheckDisposedOrStarted() + { + if (this.Disposed) { + throw new ObjectDisposedException(nameof(STJsonWriter)); + } + if (!m_b_start) { + throw new STJsonWriterException("Cannot complete the current operation. Please call StartWithObject/Array() first."); + } + } + + private STJsonWriter CreateObject(STJsonWriterStartCallback start_callback, STJsonWriterCallback callback) + { + + this.PushStack(m_current_stack.Level + 1, LevelType.Object); + + m_writer.Write('{'); + + if (start_callback != null) { + start_callback(this); + } else { + callback(); + } + int n_item_count = m_current_stack.ItemCount; + + this.PopStack(); + + if (this.SpaceCount != 0 && n_item_count != 0) { + m_writer.Write("\r\n" + m_current_stack.Space); + } + m_writer.Write('}'); + return this; + } + + private STJsonWriter CreateArray(STJsonWriterStartCallback start_callback, STJsonWriterCallback callback) + { + this.PushStack(m_current_stack.Level + 1, LevelType.Array); + + m_writer.Write('['); + + if (start_callback != null) { + start_callback(this); + } else { + callback(); + } + int n_item_count = m_current_stack.ItemCount; + + this.PopStack(); + + if (this.SpaceCount != 0 && n_item_count != 0) { + m_writer.Write("\r\n" + m_current_stack.Space); + } + m_writer.Write(']'); + return this; + } + + private void PushStack(int n_level, LevelType type) + { + var str_space = "".PadRight(n_level * this.SpaceCount); + m_current_stack = new StackInfo(n_level, type, str_space); + m_stack.Push(m_current_stack); + } + + private void PopStack() + { + m_current_stack = null; + if (m_stack.Count == 0) return; + m_stack.Pop(); + if (m_stack.Count == 0) return; + m_current_stack = m_stack.Peek(); + } + + private STJsonWriter SetKey(string str_key) + { + if (m_current_stack.LeveType != LevelType.Object) { + throw new STJsonWriterException("Current level is not a object. The current operation is only available at the object level."); + } + if (m_current_stack.ItemCount++ > 0) { + m_writer.Write(','); + } + var str_text = "\"" + STJson.Escape(str_key) + "\":"; + if (this.SpaceCount != 0) { + m_writer.Write("\r\n" + m_current_stack.Space + str_text + " "); + } else { + m_writer.Write(str_text); + } + return this; + } + } +} diff --git a/Src/STLib.Json/Exceptions/STJsonPathException.cs b/Src/STLib.Json/STJsonPath/Exceptions/STJsonPathException.cs similarity index 100% rename from Src/STLib.Json/Exceptions/STJsonPathException.cs rename to Src/STLib.Json/STJsonPath/Exceptions/STJsonPathException.cs diff --git a/Src/STLib.Json/Exceptions/STJsonPathParseException.cs b/Src/STLib.Json/STJsonPath/Exceptions/STJsonPathParseException.cs similarity index 100% rename from Src/STLib.Json/Exceptions/STJsonPathParseException.cs rename to Src/STLib.Json/STJsonPath/Exceptions/STJsonPathParseException.cs diff --git a/Src/STLib.Json/JsonPathParser/STJsonPath.DataType.cs b/Src/STLib.Json/STJsonPath/STJsonPath.DataType.cs similarity index 56% rename from Src/STLib.Json/JsonPathParser/STJsonPath.DataType.cs rename to Src/STLib.Json/STJsonPath/STJsonPath.DataType.cs index 2dde225..dd636e4 100644 --- a/Src/STLib.Json/JsonPathParser/STJsonPath.DataType.cs +++ b/Src/STLib.Json/STJsonPath/STJsonPath.DataType.cs @@ -8,88 +8,83 @@ public enum STJsonPathSelectMode ItemOnly, ItemWithPath, KeepStructure } - public class STJsonPathCallBackArgs + public class STJsonPathCallbackArgs { public bool Selected { get; set; } public STJson Path { get; internal set; } public STJson Json { get; set; } } - //public struct STJsonPathCallBackResult - //{ - // public bool Selected; - // public STJson Json; - - // public STJsonPathCallBackResult(bool selected, STJson json) { - // this.Selected = selected; - // this.Json = json; - // } - //} - public struct SelectSetting { public STJson Root; public STJsonPathSelectMode Mode; public Stack Path; - //public STJsonPathCallBackVoid CallbackVoid; - //public STJsonPathCallBack CallbackReturn; public STJsonPathCallBack Callback; - public static SelectSetting Create() { - return new SelectSetting() { + public static SelectSetting Create() + { + return new SelectSetting() + { Path = new Stack() }; } } - public delegate void STJsonPathCallBack(STJsonPathCallBackArgs args); - //public delegate void STJsonPathCallBackVoid(STJson path, STJson json); - //public delegate STJsonPathCallBackResult STJsonPathCallBack(STJson path, STJson json); - public delegate STJson STJsonPathCustomFuncHander(STJsonPathExpFuncArg[] args); + public delegate void STJsonPathCallBack(STJsonPathCallbackArgs args); + public delegate STJson STJsonPathCustomFunctionHander(STJsonPathExpressionFunctionArg[] args); - public enum STJsonPathExpFuncArgType + public enum STJsonPathExpressionFunctionArgType { Undefined, Long, Double, String, Boolean, Array, Object, } - public struct STJsonPathExpFuncArg + public struct STJsonPathExpressionFunctionArg { - public STJsonPathExpFuncArgType Type; + public STJsonPathExpressionFunctionArgType Type; public object Value; } + + + /* ================================================== internal ================================================== */ + + //========================================================== - internal delegate ExpressionToken STJsonPathBuildInFuncHander(ExpressionToken[] args); + internal delegate STJsonPathExpressionToken STJsonPathBuildInFuncHander(STJsonPathExpressionToken[] args); //========================================================== - internal enum ExpressTokenType + + internal enum STJsonPathExpressionTokenType { - Undefined, Long, Double, String, Boolean, Array, Object, ArrayExp, Func, PathItem, Operator, LeftParentheses, RightParentheses + Undefined, Long, Double, String, Boolean, Array, Object, ArrayExp, Func, PathItem, Operator, LeftParentheses, RightParentheses, Error } - internal struct ExpressionToken + internal struct STJsonPathExpressionToken { - public static ExpressionToken Undefined; - public static ExpressionToken True = ExpressionToken.Create(-1, ExpressTokenType.Boolean, true); - public static ExpressionToken False = ExpressionToken.Create(-1, ExpressTokenType.Boolean, false); + public static readonly STJsonPathExpressionToken Undefined = new STJsonPathExpressionToken() { Type = STJsonPathExpressionTokenType.Undefined }; + public static STJsonPathExpressionToken True = STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Boolean, true); + public static STJsonPathExpressionToken False = STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Boolean, false); - public bool IsNumber { - get { return this.Type == ExpressTokenType.Long || this.Type == ExpressTokenType.Double; } + public bool IsNumber + { + get { return this.Type == STJsonPathExpressionTokenType.Long || this.Type == STJsonPathExpressionTokenType.Double; } } - public string Text { + public string Text + { get { - if (this.Type == ExpressTokenType.Operator) { + if (this.Type == STJsonPathExpressionTokenType.Operator) { return " " + this.Value + " "; } - if (this.Type == ExpressTokenType.PathItem) { + if (this.Type == STJsonPathExpressionTokenType.PathItem) { string strItem = string.Empty; foreach (var v in this.PathItems) { strItem += v.Text; } return strItem.Trim(); } - if (this.Type == ExpressTokenType.Func) { + if (this.Type == STJsonPathExpressionTokenType.Func) { string strFunc = this.Value + "("; foreach (var v in this.Args) { strFunc += v.Text + ", "; @@ -104,11 +99,11 @@ public string Text { } return strFunc.Trim('.'); } - if (this.Type == ExpressTokenType.Array) { + if (this.Type == STJsonPathExpressionTokenType.Array) { List lst = new List(); - foreach (var v in this.Value as ExpressionToken[]) { + foreach (var v in this.Value as STJsonPathExpressionToken[]) { switch (v.Type) { - case ExpressTokenType.String: + case STJsonPathExpressionTokenType.String: lst.Add("'" + STJson.Escape(v.Text) + "'"); break; default: @@ -118,17 +113,17 @@ public string Text { } return "[" + string.Join(",", lst.ToArray()) + "]"; } - if (this.Type == ExpressTokenType.ArrayExp) { + if (this.Type == STJsonPathExpressionTokenType.ArrayExp) { List lst = new List(); foreach (var v in this.Value as STJsonPathExpression[]) { lst.Add(v.Text); } return "[" + string.Join(",", lst.ToArray()) + "]"; } - if (this.Type == ExpressTokenType.Undefined) { + if (this.Type == STJsonPathExpressionTokenType.Undefined) { return "undefined"; } - if (this.Type == ExpressTokenType.Boolean) { + if (this.Type == STJsonPathExpressionTokenType.Boolean) { return Convert.ToString(this.Value).ToLower(); } return Convert.ToString(this.Value); @@ -136,38 +131,54 @@ public string Text { } public int Index; - public ExpressTokenType Type; + public STJsonPathExpressionTokenType Type; public object Value; public List Args; public List PathItems; - public static ExpressionToken Create(int nIndex, ExpressTokenType type, object value) { - return new ExpressionToken() { + public static STJsonPathExpressionToken CreateError(string str_error) { + return new STJsonPathExpressionToken() + { + Index = -1, + Type = STJsonPathExpressionTokenType.Error, + Value = str_error + }; + } + + public static STJsonPathExpressionToken Create(int nIndex, STJsonPathExpressionTokenType type, object value) + { + return new STJsonPathExpressionToken() + { Index = nIndex, Type = type, Value = value }; } - public static ExpressionToken Create(int nIndex, List items) { - return new ExpressionToken() { + public static STJsonPathExpressionToken Create(int nIndex, List items) + { + return new STJsonPathExpressionToken() + { Index = nIndex, - Type = ExpressTokenType.PathItem, + Type = STJsonPathExpressionTokenType.PathItem, PathItems = items }; } - public static ExpressionToken Create(int nIndex, string strFuncName, List args, List items) { - return new ExpressionToken() { + public static STJsonPathExpressionToken Create(int nIndex, string strFuncName, List args, List items) + { + return new STJsonPathExpressionToken() + { Index = nIndex, - Type = ExpressTokenType.Func, + Type = STJsonPathExpressionTokenType.Func, Value = strFuncName, Args = args, PathItems = items }; } - public override string ToString() { + public override string ToString() + { return "[" + this.Type.ToString() + "] - (" + this.Value + ")"; } } @@ -183,21 +194,25 @@ internal struct STJsonPathToken public string Value; public STJsonPathTokenType Type; - public bool IsNumber { + public bool IsNumber + { get { return this.Type == STJsonPathTokenType.Long || this.Type == STJsonPathTokenType.Double; } } - public STJsonPathToken(int nIndex, string strValue, STJsonPathTokenType type) { + public STJsonPathToken(int nIndex, string strValue, STJsonPathTokenType type) + { this.Index = nIndex; this.Value = strValue; this.Type = type; } - public bool IsSymbol(string strSmb) { + public bool IsSymbol(string strSmb) + { return this.Value == strSmb && this.Type != STJsonPathTokenType.String; } - public override string ToString() { + public override string ToString() + { return string.Format("\"{0}\" - [{1}] - [{2}]", this.Value, this.Index, this.Type); } } diff --git a/Src/STLib.Json/STJsonPath.Static.cs b/Src/STLib.Json/STJsonPath/STJsonPath.Static.cs similarity index 90% rename from Src/STLib.Json/STJsonPath.Static.cs rename to Src/STLib.Json/STJsonPath/STJsonPath.Static.cs index 5443d5a..abc58d7 100644 --- a/Src/STLib.Json/STJsonPath.Static.cs +++ b/Src/STLib.Json/STJsonPath/STJsonPath.Static.cs @@ -8,16 +8,18 @@ namespace STLib.Json { public partial class STJsonPath { - public static Dictionary CustomFunctions { get; private set; } + public static Dictionary CustomFunctions { get; private set; } internal static Dictionary BuildInFunctions { get; private set; } - static STJsonPath() { + static STJsonPath() + { ME.BuildInFunctions = new Dictionary(); - ME.CustomFunctions = new Dictionary(); + ME.CustomFunctions = new Dictionary(); STJsonPathBuildInFunctions.Init(); } - private static STJson GetParsedTokens(List pathItems) { + private static STJson GetParsedTokens(List pathItems) + { STJson json = STJson.CreateArray(); foreach (var v in pathItems) { ME.GetParsedTokens(json, v); @@ -25,7 +27,8 @@ private static STJson GetParsedTokens(List pathItems) { return json; } - private static void GetParsedTokens(STJson json, STJsonPathItem item) { + private static void GetParsedTokens(STJson json, STJsonPathItem item) + { STJson jsonTemp = null; switch (item.Type) { case STJsonPathItem.ItemType.Root: @@ -59,7 +62,8 @@ private static void GetParsedTokens(STJson json, STJsonPathItem item) { } } - private static STJson GetParseExpress(STJsonPathExpression express) { + private static STJson GetParseExpress(STJsonPathExpression express) + { var json_items = STJson.CreateArray(); STJson json_array = null; var json = new STJson(); @@ -68,36 +72,37 @@ private static STJson GetParseExpress(STJsonPathExpression express) { json.SetItem("items", json_items); foreach (var v in express.Express) { switch (v.Type) { - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: json_items.Append(STJson.FromObject(new { type = "expression_item", item_type = "operator", value = v.Value })); break; - case ExpressTokenType.String: + case STJsonPathExpressionTokenType.String: json_items.Append(STJson.FromObject(new { type = "expression_item", item_type = "string", value = v.Value })); break; - case ExpressTokenType.Long: + case STJsonPathExpressionTokenType.Long: json_items.Append(STJson.FromObject(new { type = "expression_item", item_type = "long", value = v.Value })); break; - case ExpressTokenType.Double: + case STJsonPathExpressionTokenType.Double: json_items.Append(STJson.FromObject(new { type = "expression_item", item_type = "double", value = v.Value })); break; - case ExpressTokenType.Boolean: + case STJsonPathExpressionTokenType.Boolean: json_items.Append(STJson.FromObject(new { type = "expression_item", item_type = "boolean", value = v.Value })); break; - case ExpressTokenType.Array: + case STJsonPathExpressionTokenType.Array: json_array = STJson.CreateArray(); json_items.Append(STJson.FromObject(new { type = "expression_item", item_type = "array", value = json_array })); foreach (STJsonPathExpression exp in v.Value as STJsonPathExpression[]) { json_array.Append(ME.GetParseExpress(exp)); } break; - case ExpressTokenType.PathItem: - json_items.Append(STJson.FromObject(new { + case STJsonPathExpressionTokenType.PathItem: + json_items.Append(STJson.FromObject(new + { type = "expression_item", item_type = "selector", items = v.PathItems == null || v.PathItems.Count == 0 ? null : ME.GetParsedTokens(v.PathItems) })); break; - case ExpressTokenType.Func: + case STJsonPathExpressionTokenType.Func: json_items.Append(ME.GetParseExpressFunction(v)); break; } @@ -105,7 +110,8 @@ private static STJson GetParseExpress(STJsonPathExpression express) { return json; } - private static STJson GetParseExpressFunction(ExpressionToken token) { + private static STJson GetParseExpressFunction(STJsonPathExpressionToken token) + { var strText = string.Empty; STJson json_arg = null; if (token.Args != null && token.Args.Count > 0) { @@ -136,20 +142,21 @@ private static STJson GetParseExpressFunction(ExpressionToken token) { .SetItem("selector", json_selector); } - internal static void GetSTJsons(List items, int nIndex, STJson jsonCurrent, SelectSetting setting, STJson jsonOut) { + internal static void GetSTJsons(List items, int nIndex, STJson jsonCurrent, SelectSetting setting, STJson jsonOut) + { if (nIndex == items.Count) { ME.OnSelectedItem(jsonCurrent, setting, jsonOut); return; } int nLoopIndex = 0; - ExpressionToken exp_ret = ExpressionToken.Undefined; + STJsonPathExpressionToken exp_ret = STJsonPathExpressionToken.Undefined; var item = items[nIndex]; var nIndexSliceL = item.Start; var nIndexSliceR = item.End; switch (item.Type) { case STJsonPathItem.ItemType.Root: case STJsonPathItem.ItemType.Current: - setting.Path.Push(item.Type == STJsonPathItem.ItemType.Root ? "$" : "@"); // TODO: Path = null + setting.Path.Push(item.Type == STJsonPathItem.ItemType.Root ? "$" : "@"); ME.GetSTJsons( items, nIndex + 1, @@ -227,7 +234,7 @@ internal static void GetSTJsons(List items, int nIndex, STJson j break; case STJsonPathItem.ItemType.Express: exp_ret = item.Express.Excute(setting.Root, jsonCurrent); - if (exp_ret.Type == ExpressTokenType.Undefined || exp_ret.Value == null) { + if (exp_ret.Type == STJsonPathExpressionTokenType.Error || exp_ret.Type == STJsonPathExpressionTokenType.Undefined || exp_ret.Value == null) { return; } if (item.Express.IsFilter) { @@ -237,7 +244,7 @@ internal static void GetSTJsons(List items, int nIndex, STJson j ME.GetSTJsons(items, nIndex + 1, jsonCurrent, setting, jsonOut); } else { if (jsonCurrent == null) return; - if (exp_ret.Type == ExpressTokenType.Long) { + if (exp_ret.Type == STJsonPathExpressionTokenType.Long) { if (jsonCurrent.ValueType != STJsonValueType.Array) { return; } @@ -249,7 +256,7 @@ internal static void GetSTJsons(List items, int nIndex, STJson j ME.GetSTJsons(items, nIndex + 1, jsonCurrent[nIndexSliceL], setting, jsonOut); setting.Path.Pop(); } - if (exp_ret.Type == ExpressTokenType.String) { + if (exp_ret.Type == STJsonPathExpressionTokenType.String) { jsonCurrent = jsonCurrent[exp_ret.Value.ToString()]; if (jsonCurrent == null) { return; @@ -258,11 +265,11 @@ internal static void GetSTJsons(List items, int nIndex, STJson j ME.GetSTJsons(items, nIndex + 1, jsonCurrent, setting, jsonOut); setting.Path.Pop(); } - if (exp_ret.Type == ExpressTokenType.Array) { - foreach (var v in exp_ret.Value as ExpressionToken[]) { + if (exp_ret.Type == STJsonPathExpressionTokenType.Array) { + foreach (var v in exp_ret.Value as STJsonPathExpressionToken[]) { switch (jsonCurrent.ValueType) { case STJsonValueType.Object: - if (v.Type != ExpressTokenType.String || v.Value == null) { + if (v.Type != STJsonPathExpressionTokenType.String || v.Value == null) { continue; } if (jsonCurrent[v.Value.ToString()] == null) { @@ -273,7 +280,7 @@ internal static void GetSTJsons(List items, int nIndex, STJson j setting.Path.Pop(); continue; case STJsonValueType.Array: - if (v.Type != ExpressTokenType.Long || v.Value == null) { + if (v.Type != STJsonPathExpressionTokenType.Long || v.Value == null) { continue; } nIndexSliceL = Convert.ToInt32(v.Value); @@ -292,7 +299,8 @@ internal static void GetSTJsons(List items, int nIndex, STJson j } } - internal static void OnSelectedItem(STJson item, SelectSetting setting, STJson jsonOut) { + internal static void OnSelectedItem(STJson item, SelectSetting setting, STJson jsonOut) + { STJson jsonPath = STJson.CreateArray(); Dictionary> dic_index = new Dictionary>(); switch (setting.Mode) { @@ -303,13 +311,14 @@ internal static void OnSelectedItem(STJson item, SelectSetting setting, STJson j } break; } - if (setting.Callback != null){ + if (setting.Callback != null) { if (jsonPath.Count == 0) { foreach (var v in setting.Path.Reverse()) { jsonPath.Append(v); } } - var arg = new STJsonPathCallBackArgs() { + var arg = new STJsonPathCallbackArgs() + { Selected = true, Path = jsonPath, Json = item @@ -352,7 +361,8 @@ internal static void OnSelectedItem(STJson item, SelectSetting setting, STJson j } } - internal static STJson CreatePathJson(STJson value, List items) { + internal static STJson CreatePathJson(STJson value, List items) + { STJson jsonOut = new STJson(); Stack stackPath = new Stack(); //ME.CreatePathJson((p, j) => { @@ -361,27 +371,31 @@ internal static STJson CreatePathJson(STJson value, List items) // Json = value // }; //}, stackPath, items, 0, jsonOut); - ME.CreatePathJson((arg) => { + ME.CreatePathJson((arg) => + { arg.Selected = true; arg.Json = value; }, stackPath, items, 0, jsonOut); return jsonOut; } - internal static STJson CreatePathJson(STJsonPathCallBack callBack, List items) { + internal static STJson CreatePathJson(STJsonPathCallBack callBack, List items) + { STJson jsonOut = new STJson(); Stack stackPath = new Stack(); ME.CreatePathJson(callBack, stackPath, items, 0, jsonOut); return jsonOut; } - internal static void CreatePathJson(STJsonPathCallBack callBack, Stack stackPath, List items, int nIndex, STJson jsonOut) { + internal static void CreatePathJson(STJsonPathCallBack callBack, Stack stackPath, List items, int nIndex, STJson jsonOut) + { if (nIndex >= items.Count) { STJson jsonPath = STJson.CreateArray(); foreach (var v in stackPath.Reverse()) { jsonPath.Append(v); } - var arg = new STJsonPathCallBackArgs() { + var arg = new STJsonPathCallbackArgs() + { Selected = true, Path = jsonPath }; @@ -434,13 +448,15 @@ internal static void CreatePathJson(STJsonPathCallBack callBack, Stack s } } - public static STJson RestorePathJson(STJson jsonItemWithPath) { + public static STJson RestorePathJson(STJson jsonItemWithPath) + { STJson jsonResult = new STJson(); ME.RestorePathJson(jsonResult, jsonItemWithPath); return jsonResult; } - public static void RestorePathJson(STJson jsonResult, STJson jsonItemWithPath) { + public static void RestorePathJson(STJson jsonResult, STJson jsonItemWithPath) + { Dictionary> dicIndices = new Dictionary>(); foreach (var item in jsonItemWithPath) { var path = item["path"]; @@ -476,7 +492,8 @@ public static void RestorePathJson(STJson jsonResult, STJson jsonItemWithPath) { } } - public static STJson TestExpression(STJson jsonRoot, STJson jsonCurrent, string strExpression) { + public static STJson TestExpression(STJson jsonRoot, STJson jsonCurrent, string strExpression) + { STJson jsonResult = new STJson(); var tokens = STJsonPathTokenizer.GetTokens(strExpression); var expression = STJsonPathExpressParser.GetSTJsonPathExpress(tokens, false); @@ -490,7 +507,8 @@ public static STJson TestExpression(STJson jsonRoot, STJson jsonCurrent, string return jsonResult; } - public static STJson GetBuildInFunctionList() { + public static STJson GetBuildInFunctionList() + { return STJsonPathBuildInFunctions.FuncList.Clone(); } } diff --git a/Src/STLib.Json/STJsonPath.cs b/Src/STLib.Json/STJsonPath/STJsonPath.cs similarity index 88% rename from Src/STLib.Json/STJsonPath.cs rename to Src/STLib.Json/STJsonPath/STJsonPath.cs index db594b3..18f8a75 100644 --- a/Src/STLib.Json/STJsonPath.cs +++ b/Src/STLib.Json/STJsonPath/STJsonPath.cs @@ -55,26 +55,11 @@ public STJson Select(STJson json, STJsonPathSelectMode model, STJsonPathCallBack Root = json, Mode = model, Path = new Stack(), - //CallbackReturn = callBack Callback = callBack }; return this.Select(json, setting); } - - //public STJson Select(STJson json, STJsonPathCallBackVoid callBack) { - // return this.Select(json, STJsonPathSelectMode.ItemOnly, callBack); - //} - - //public STJson Select(STJson json, STJsonPathSelectMode model, STJsonPathCallBackVoid callBack) { - // SelectSetting setting = new SelectSetting() { - // Root = json, - // Mode = model, - // Path = new Stack(), - // CallbackVoid = callBack - // }; - // return this.Select(json, setting); - //} - + private STJson Select(STJson json, SelectSetting setting) { STJson jsonResult = STJson.CreateArray(); ME.GetSTJsons(m_lst_items, 0, json, setting, jsonResult); @@ -108,7 +93,7 @@ public STJson SelectLast(STJson json, STJsonPathSelectMode model) { return json_result[json_result.Count - 1]; } - + public STJson CreatePathJson(string value) { return STJsonPath.CreatePathJson(STJson.FromObject(value), m_lst_items); } @@ -137,14 +122,14 @@ public STJson CreatePathJson(DateTime value) { return STJsonPath.CreatePathJson(STJson.FromObject(value), m_lst_items); } - public STJson CreatePathJson(object value) { - return STJsonPath.CreatePathJson(STJson.FromObject(value), m_lst_items); - } - public STJson CreatePathJson(STJson value) { return STJsonPath.CreatePathJson(value, m_lst_items); } + public STJson CreatePathJson(object value) { + return STJsonPath.CreatePathJson(STJson.FromObject(value), m_lst_items); + } + public STJson CreatePathJson(STJsonPathCallBack callBack) { return STJsonPath.CreatePathJson(callBack, m_lst_items); } diff --git a/Src/STLib.Json/STJsonPath/STJsonPathBuildInFunctions.cs b/Src/STLib.Json/STJsonPath/STJsonPathBuildInFunctions.cs new file mode 100644 index 0000000..8fc2a36 --- /dev/null +++ b/Src/STLib.Json/STJsonPath/STJsonPathBuildInFunctions.cs @@ -0,0 +1,531 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using ME = STLib.Json.STJsonPathBuildInFunctions; + +namespace STLib.Json +{ + internal class STJsonPathBuildInFunctions + { + private delegate STJsonPathExpressionToken GeneralCallback(STJsonPathExpressionToken token); + + private static List m_lst_info; + + public static STJson FuncList { get; private set; } + + [STJson(STJsonSerializeMode.Exclude)] + private struct FNInfo + { + public string name; + public string[] demos; + [STJsonProperty] + public STJsonPathBuildInFuncHander func; + + public FNInfo(string strName, STJsonPathBuildInFuncHander fn, string[] demos) + { + this.name = strName; + this.func = fn; + this.demos = demos; + } + } + + public static void Init() + { + m_lst_info = new List(); + m_lst_info.Add(new FNInfo("typeof", ME.FN_typeof, "(object) -> typeof('abc')".Split('|'))); + m_lst_info.Add(new FNInfo("str", ME.FN_str, "(object) -> str(123)".Split('|'))); + m_lst_info.Add(new FNInfo("upper", ME.FN_upper, "(string) -> upper('abc')".Split('|'))); + m_lst_info.Add(new FNInfo("lower", ME.FN_lower, "(string) -> lower('abc')".Split('|'))); + + m_lst_info.Add(new FNInfo("len", ME.FN_len, "(string|array) -> len('abc')".Split('|'))); + m_lst_info.Add(new FNInfo("long", ME.FN_long, "(object) -> long('123')".Split('|'))); + m_lst_info.Add(new FNInfo("double", ME.FN_double, "(object) -> double('123')".Split('|'))); + m_lst_info.Add(new FNInfo("bool", ME.FN_bool, "(object) -> bool('abc')".Split('|'))); + + m_lst_info.Add(new FNInfo("abs", ME.FN_abs, "(number) -> abs(-123)".Split('|'))); + m_lst_info.Add(new FNInfo("round", ME.FN_round, "(number) -> round(123.4)".Split('|'))); + m_lst_info.Add(new FNInfo("ceil", ME.FN_lower, "(number) -> ceil(123.4)".Split('|'))); + + m_lst_info.Add(new FNInfo("max", ME.FN_max, "(array) -> max([1,2,3,4,5])".Split('|'))); + m_lst_info.Add(new FNInfo("min", ME.FN_min, "(array) -> max([1,2,3,4,5])".Split('|'))); + m_lst_info.Add(new FNInfo("avg", ME.FN_avg, "(array) -> max([1,2,3,4,5])".Split('|'))); + m_lst_info.Add(new FNInfo("sum", ME.FN_sum, "(array) -> max([1,2,3,4,5])".Split('|'))); + + m_lst_info.Add(new FNInfo("trim", ME.FN_trim, "(string) -> trim('abc')|(string,string) -> trim(',.abc.,','.,')".Split('|'))); + m_lst_info.Add(new FNInfo("trims", ME.FN_trims, "(string) -> trims('abc')|(string,string) -> trims(',.abc.,','.,')".Split('|'))); + m_lst_info.Add(new FNInfo("trime", ME.FN_trime, "(string) -> trime('abc')|(string,string) -> trime(',.abc.,','.,')".Split('|'))); + m_lst_info.Add(new FNInfo("split", ME.FN_split, @"(string,regex_string) -> split('a.b.c','\\.')".Split('|'))); + + m_lst_info.Add(new FNInfo("time", ME.FN_time, "() -> time()|(string) -> time('yyyy-MM-dd')|(long,string) -> time(1684751411726,'yyyy-MM-dd')".Split('|'))); + + foreach (var v in m_lst_info) { + STJsonPath.BuildInFunctions.Add(v.name, v.func); + } + + ME.FuncList = STJson.FromObject(m_lst_info, STJsonSetting.Default); + } + + public static STJsonPathExpressionToken FN_typeof(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: typeof(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_typeof(args[0]); + } + + private static STJsonPathExpressionToken FN_typeof(STJsonPathExpressionToken token) + { + switch (token.Type) { + case STJsonPathExpressionTokenType.String: return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "string"); + case STJsonPathExpressionTokenType.Long: return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "long"); + case STJsonPathExpressionTokenType.Double: return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "double"); + case STJsonPathExpressionTokenType.Boolean: return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "boolean"); + case STJsonPathExpressionTokenType.Array: return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "array"); + case STJsonPathExpressionTokenType.Object: return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "object"); + } + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, "unknows"); + } + + public static STJsonPathExpressionToken FN_str(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: str(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_str(args[0]); + } + + private static STJsonPathExpressionToken FN_str(STJsonPathExpressionToken token) + { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, token.Text); + } + + public static STJsonPathExpressionToken FN_upper(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: upper(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_upper(args[0]); + } + + private static STJsonPathExpressionToken FN_upper(STJsonPathExpressionToken token) + { + if (token.Value == null) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, null); + } + switch (token.Type) { + case STJsonPathExpressionTokenType.String: + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, Convert.ToString(token.Value).ToUpper()); + } + return STJsonPathExpressionToken.CreateError("Can not match the function: upper(" + token.Type + "). "); + } + + public static STJsonPathExpressionToken FN_lower(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: lower(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_lower(args[0]); + } + + private static STJsonPathExpressionToken FN_lower(STJsonPathExpressionToken token) + { + if (token.Value == null) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, null); + } + switch (token.Type) { + case STJsonPathExpressionTokenType.String: + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, Convert.ToString(token.Value).ToLower()); + } + return STJsonPathExpressionToken.CreateError("Can not match the function: lower(" + token.Type + "). "); + } + + public static STJsonPathExpressionToken FN_len(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: len(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_len(args[0]); + } + + private static STJsonPathExpressionToken FN_len(STJsonPathExpressionToken token) + { + if (token.Value == null) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, 0); + } + switch (token.Type) { + case STJsonPathExpressionTokenType.String: + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, Convert.ToString(token.Value).Length); + case STJsonPathExpressionTokenType.Array: + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, (token.Value as STJsonPathExpressionToken[]).Length); + } + return STJsonPathExpressionToken.CreateError("Can not match the function: len(" + token.Type + "). "); + } + + public static STJsonPathExpressionToken FN_long(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: long(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_long(args[0]); + } + + private static STJsonPathExpressionToken FN_long(STJsonPathExpressionToken token) + { + if (token.Value == null) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, 0); + } + try { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, Convert.ToInt64(token.Value)); + } catch (Exception ex) { + return STJsonPathExpressionToken.CreateError("Get an error when excute function: long(" + token.Type + "). " + ex.Message); + } + } + + public static STJsonPathExpressionToken FN_double(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: double(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_double(args[0]); + } + + private static STJsonPathExpressionToken FN_double(STJsonPathExpressionToken token) + { + if (token.Value == null) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, 0); + } + try { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, Convert.ToDouble(token.Value)); + } catch (Exception ex) { + return STJsonPathExpressionToken.CreateError("Get an error when excute function: double(" + token.Type + "). " + ex.Message); + } + } + + public static STJsonPathExpressionToken FN_bool(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: bool(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_bool(args[0]); + } + + private static STJsonPathExpressionToken FN_bool(STJsonPathExpressionToken token) + { + try { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Boolean, Convert.ToBoolean(token.Value)); + } catch { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Boolean, false); + } + } + + public static STJsonPathExpressionToken FN_abs(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: abs(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_abs(args[0]); + } + + private static STJsonPathExpressionToken FN_abs(STJsonPathExpressionToken token) + { + long l_v = 0; + double d_v = 0; + switch (token.Type) { + case STJsonPathExpressionTokenType.Long: + l_v = Convert.ToInt64(token.Value); + if (l_v < 0) { + token.Value = -l_v; + } + return token; + case STJsonPathExpressionTokenType.Double: + d_v = Convert.ToDouble(token.Value); + if (d_v < 0) { + token.Value = -d_v; + } + return token; + default: + return STJsonPathExpressionToken.CreateError("Can not match the function: abs(" + token.Type + "). "); + } + } + + public static STJsonPathExpressionToken FN_round(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: round(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_round(args[0]); + } + + private static STJsonPathExpressionToken FN_round(STJsonPathExpressionToken token) + { + switch (token.Type) { + case STJsonPathExpressionTokenType.Long: return token; + case STJsonPathExpressionTokenType.Double: + token.Type = STJsonPathExpressionTokenType.Long; + token.Value = (long)Math.Round(Convert.ToDouble(token.Value)); + return token; + default: + return STJsonPathExpressionToken.CreateError("Can not match the function: round(" + token.Type + "). "); + } + } + + public static STJsonPathExpressionToken FN_ceil(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1) { + return STJsonPathExpressionToken.CreateError("Can not match the function: ceil(" + ME.GetFunctionArgTypeList(args) + "). "); + } + return ME.FN_ceil(args[0]); + } + + private static STJsonPathExpressionToken FN_ceil(STJsonPathExpressionToken token) + { + switch (token.Type) { + case STJsonPathExpressionTokenType.Long: return token; + case STJsonPathExpressionTokenType.Double: + token.Type = STJsonPathExpressionTokenType.Long; + token.Value = (long)Math.Ceiling(Convert.ToDouble(token.Value)); + return token; + default: + return STJsonPathExpressionToken.CreateError("Can not match the function: ceil(" + token.Type + "). "); + } + } + + + private static STJsonPathExpressionToken FN_max(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1 || args[0].Type != STJsonPathExpressionTokenType.Array) { + return STJsonPathExpressionToken.CreateError("Can not match the function: max(" + ME.GetFunctionArgTypeList(args) + "). "); + } + STJsonPathExpressionToken token = STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, double.MinValue); + double d_v = double.MinValue, d_t = 0; + foreach (var v in args[0].Value as STJsonPathExpressionToken[]) { + if (!v.IsNumber) { + continue; + } + d_t = Convert.ToDouble(v.Value); + if (d_t > d_v) { + token = v; + d_v = d_t; + } + } + return token; + } + + private static STJsonPathExpressionToken FN_min(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1 || args[0].Type != STJsonPathExpressionTokenType.Array) { + return STJsonPathExpressionToken.CreateError("Can not match the function: min(" + ME.GetFunctionArgTypeList(args) + "). "); + } + STJsonPathExpressionToken token = STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, double.MaxValue); + double d_v = double.MaxValue, d_t = 0; + foreach (var v in args[0].Value as STJsonPathExpressionToken[]) { + if (!v.IsNumber) { + continue; + } + d_t = Convert.ToDouble(v.Value); + if (d_t < d_v) { + token = v; + d_v = d_t; + } + } + return token; + } + + private static STJsonPathExpressionToken FN_avg(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1 || args[0].Type != STJsonPathExpressionTokenType.Array) { + return STJsonPathExpressionToken.CreateError("Can not match the function: avg(" + ME.GetFunctionArgTypeList(args) + "). "); + } + double d_v = 0; + int nCounter = 0; + foreach (var v in args[0].Value as STJsonPathExpressionToken[]) { + if (!v.IsNumber) { + continue; + } + nCounter++; + d_v += Convert.ToDouble(v.Value); + } + if (nCounter == 0) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, 0); + } + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, d_v / nCounter); + } + + private static STJsonPathExpressionToken FN_sum(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length != 1 || args[0].Type != STJsonPathExpressionTokenType.Array) { + return STJsonPathExpressionToken.CreateError("Can not match the function: sum(" + ME.GetFunctionArgTypeList(args) + "). "); + } + double d_v = 0; + foreach (var v in args[0].Value as STJsonPathExpressionToken[]) { + if (!v.IsNumber) { + continue; + } + d_v += Convert.ToDouble(v.Value); + } + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, d_v); + } + + + public static STJsonPathExpressionToken FN_split(STJsonPathExpressionToken[] args) + { + bool b_flag = + args == null || + args.Length != 2 || + args[0].Type != STJsonPathExpressionTokenType.String || + args[2].Type != STJsonPathExpressionTokenType.String; + if (b_flag) { + return STJsonPathExpressionToken.CreateError("Can not match the function: abs(" + ME.GetFunctionArgTypeList(args) + "). "); + } + try { + Regex reg = new Regex(args[1].Value.ToString()); + var strs = reg.Split(args[0].Value.ToString()); + var results = new STJsonPathExpressionToken[strs.Length]; + for (int i = 0; i < strs.Length; i++) { + results[i] = STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, strs[i]); + } + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Array, results); + } catch (Exception ex) { + return STJsonPathExpressionToken.CreateError("Get an error when excute function: split(" + ME.GetFunctionArgTypeList(args) + "). " + ex.Message); + } + } + + public static STJsonPathExpressionToken FN_trim(STJsonPathExpressionToken[] args) + { + return ME.FN_trim(args, 0); + } + + private static STJsonPathExpressionToken FN_trims(STJsonPathExpressionToken[] args) + { + return ME.FN_trim(args, 1); + } + + private static STJsonPathExpressionToken FN_trime(STJsonPathExpressionToken[] args) + { + return ME.FN_trim(args, 2); + } + + private static STJsonPathExpressionToken FN_trim(STJsonPathExpressionToken[] args, int nModel) + { + if (args == null || args.Length < 1) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, null); + } + if (args[0].Type != STJsonPathExpressionTokenType.String) { + return STJsonPathExpressionToken.CreateError("Can not match the function: trim*(" + ME.GetFunctionArgTypeList(args) + "). "); + } + char[] arr_ch_trim = new char[] { ' ', '\t', '\r', '\n' }; ; + try { + if (args.Length == 2 && args[1].Type == STJsonPathExpressionTokenType.String) { + var strText = args[1].Value.ToString(); + arr_ch_trim = new char[strText.Length]; + for (int i = 0; i < strText.Length; i++) { + arr_ch_trim[i] = strText[i]; + } + } else { + return STJsonPathExpressionToken.CreateError("Can not match the function: trim*(" + ME.GetFunctionArgTypeList(args) + "). "); + } + switch (nModel) { + case 0: + args[0].Value = args[0].Value.ToString().Trim(arr_ch_trim); + break; + case 1: + args[0].Value = args[0].Value.ToString().TrimStart(arr_ch_trim); + break; + case 2: + args[0].Value = args[0].Value.ToString().TrimEnd(arr_ch_trim); + break; + } + return args[0]; + } catch (Exception ex) { + return STJsonPathExpressionToken.CreateError("Get an error when excute function: time*(" + ME.GetFunctionArgTypeList(args) + "). " + ex.Message); + } + } + + public static STJsonPathExpressionToken FN_time(STJsonPathExpressionToken[] args) + { + if (args == null || args.Length == 0) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, (long)(DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified)).TotalMilliseconds); + } + if (args.Length == 1) { + if (args[0].Type == STJsonPathExpressionTokenType.String) { + try { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, DateTime.Now.ToString(args[0].Value.ToString())); + } catch (Exception ex) { + return STJsonPathExpressionToken.CreateError("Get an error when excute function: time(" + ME.GetFunctionArgTypeList(args) + "). " + ex.Message); + } + } + } + if (args.Length == 2) { + if (args[0].Type == STJsonPathExpressionTokenType.Long && args[1].Type == STJsonPathExpressionTokenType.String) { + var strTime = + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified) + .AddMilliseconds(Convert.ToInt64(args[0].Value)) + .ToLocalTime() + .ToString(args[1].Value.ToString()); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, strTime); + } + } + return STJsonPathExpressionToken.CreateError("Can not match the function: time(" + ME.GetFunctionArgTypeList(args) + ")"); + } + + internal static string GetFunctionArgTypeList(STJsonPathExpressionToken[] args) + { + string[] arr_str = new string[args.Length]; + for (int i = 0; i < args.Length; i++) { + arr_str[i] = args[i].Type.ToString(); + } + return string.Join(",", arr_str); + } + + //private static ExpressionToken FNGeneral(ExpressionToken[] args, GeneralCallback callback) + //{ + // if (args == null || args.Length < 1) { + // return ExpressionToken.Undefined; + // } + // var b_depth = args[0].Type == ExpressTokenType.Array; + // if (b_depth) { + // var arr = ME.GetArgs(args[0]); + // return ME.FNGeneralArray(arr, callback); + // } + // return callback(args[0]); + //} + + ////private static ExpressionToken FNGeneral(ExpressionToken[] args, GeneralCallback callBack, bool bDepth) + ////{ + //// if (args == null || args.Length < 1) { + //// return ExpressionToken.Undefined; + //// } + //// if (args.Length > 1) { + //// bDepth = (bool)ME.FN_bool(args[1]).Value; + //// } + //// //bDepth |= args[0].Type == ExpressTokenType.Array; + //// if (bDepth) { + //// var arr = ME.GetArgs(args[0]); + //// return ME.FNGeneralArray(arr, callBack); + //// } + //// return callBack(args[0]); + ////} + + //private static ExpressionToken FNGeneralArray(ExpressionToken[] tokens, GeneralCallback callBack) + //{ + // ExpressionToken[] results = new ExpressionToken[tokens.Length]; + // for (int i = 0; i < tokens.Length; i++) { + // if (tokens[i].Type == ExpressTokenType.Array) { + // results[i] = ME.FNGeneralArray(tokens[i].Value as ExpressionToken[], callBack); + // } else { + // results[i] = callBack(tokens[i]); + // } + // } + // return ExpressionToken.Create(-1, ExpressTokenType.Array, results); + //} + + //private static ExpressionToken[] GetArgs(ExpressionToken token) + //{ + // if (token.Type == ExpressTokenType.Array) { + // return token.Value as ExpressionToken[]; + // } + // return new ExpressionToken[] { token }; + //} + } +} diff --git a/Src/STLib.Json/JsonPathParser/STJsonPathExpressParser.cs b/Src/STLib.Json/STJsonPath/STJsonPathExpressParser.cs similarity index 76% rename from Src/STLib.Json/JsonPathParser/STJsonPathExpressParser.cs rename to Src/STLib.Json/STJsonPath/STJsonPathExpressParser.cs index 6a21f37..5551364 100644 --- a/Src/STLib.Json/JsonPathParser/STJsonPathExpressParser.cs +++ b/Src/STLib.Json/STJsonPath/STJsonPathExpressParser.cs @@ -42,7 +42,7 @@ public static STJsonPathExpression GetSTJsonPathExpress(List to } var lst_exp_token = ME.GetExpressTokens(tokens); foreach (var v in lst_exp_token) { - if (v.Type == ExpressTokenType.String) { + if (v.Type == STJsonPathExpressionTokenType.String) { strText += "'" + STJson.Escape(v.Text) + "'"; } else { strText += v.Text; @@ -53,26 +53,26 @@ public static STJsonPathExpression GetSTJsonPathExpress(List to return new STJsonPathExpression("{" + strText + "}", lst, isFilter); } - public static List GetExpressTokens(List tokens) { + public static List GetExpressTokens(List tokens) { STJsonPathToken token_next = new STJsonPathToken(); - List lst_result = new List(); + List lst_result = new List(); List lst_range = null; List lst_selector_tokens = new List(); for (int i = 0; i < tokens.Count; i++) { var token = tokens[i]; if (m_dic_op_priority.ContainsKey(token.Value)) { - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.Operator, token.Value)); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.Operator, token.Value)); continue; } switch (token.Type) { case STJsonPathTokenType.Long: - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.Long, long.Parse(token.Value))); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.Long, long.Parse(token.Value))); continue; case STJsonPathTokenType.Double: - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.Double, double.Parse(token.Value))); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.Double, double.Parse(token.Value))); continue; case STJsonPathTokenType.String: - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.String, token.Value)); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.String, token.Value)); continue; case STJsonPathTokenType.Property: if (i + 1 < tokens.Count) { @@ -90,25 +90,25 @@ public static List GetExpressTokens(List token if (lst_range.Count < 1) { throw new STJsonPathParseException(token.Index, "The bracket is empty. index: " + token.Index); } - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.ArrayExp, ME.GetExpressesFromRange(lst_range).ToArray())); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.ArrayExp, ME.GetExpressesFromRange(lst_range).ToArray())); i += lst_range.Count + 1; continue; } switch (token.Value) { case "(": - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.LeftParentheses, "(")); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.LeftParentheses, "(")); continue; case ")": - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.RightParentheses, ")")); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.RightParentheses, ")")); continue; case "true": - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.Boolean, true)); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.Boolean, true)); continue; case "false": - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.Boolean, false)); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.Boolean, false)); continue; case "null": - lst_result.Add(ExpressionToken.Create(token.Index, ExpressTokenType.Object, null)); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathExpressionTokenType.Object, null)); continue; case "$": case "@": @@ -133,7 +133,7 @@ public static List GetExpressTokens(List token lst_selector_tokens.Add(tokens[i++]); } i--; - lst_result.Add(ExpressionToken.Create(token.Index, STJsonPathParser.GetPathItems(lst_selector_tokens))); + lst_result.Add(STJsonPathExpressionToken.Create(token.Index, STJsonPathParser.GetPathItems(lst_selector_tokens))); continue; default: throw new STJsonPathParseException(token.Index, "Unknows token [" + token.Value + "]"); @@ -142,14 +142,14 @@ public static List GetExpressTokens(List token return lst_result; } - private static ExpressionToken GetFuncToken(List tokens, ref int nIndex) { + private static STJsonPathExpressionToken GetFuncToken(List tokens, ref int nIndex) { var strFuncName = tokens[nIndex].Value; var nFirstIndex = nIndex; var lst_range = STJsonPathTokenizer.GetRange(tokens, nIndex + 1, "(", ")"); var lst_exp_arg = lst_range.Count == 0 ? new List() : ME.GetExpressesFromRange(lst_range); nIndex += lst_range.Count + 3; if (nIndex >= tokens.Count || tokens[nIndex].Type == STJsonPathTokenType.Operator) { - return ExpressionToken.Create(nFirstIndex, strFuncName, lst_exp_arg, null); + return STJsonPathExpressionToken.Create(nFirstIndex, strFuncName, lst_exp_arg, null); } int nA = 0, nB = 0; List lst_token_temp = new List(); @@ -188,7 +188,7 @@ private static ExpressionToken GetFuncToken(List tokens, ref in lst_token_temp.Add(tokens[i]); } var items = STJsonPathParser.GetPathItems(lst_token_temp); - return ExpressionToken.Create(nFirstIndex, strFuncName, lst_exp_arg, items); + return STJsonPathExpressionToken.Create(nFirstIndex, strFuncName, lst_exp_arg, items); } public static List GetExpressesFromRange(List tokens) { @@ -238,23 +238,23 @@ public static List GetExpressesFromRange(List GetExpressPolishQueue(List tokens) { - Stack stack_sop = new Stack(); - List lst_out = new List(); - ExpressionToken sop_top = ExpressionToken.Undefined; + public static List GetExpressPolishQueue(List tokens) { + Stack stack_sop = new Stack(); + List lst_out = new List(); + STJsonPathExpressionToken sop_top = STJsonPathExpressionToken.Undefined; string str_last_op = string.Empty; foreach (var token in tokens) { switch (token.Type) { - case ExpressTokenType.LeftParentheses: + case STJsonPathExpressionTokenType.LeftParentheses: stack_sop.Push(token); continue; - case ExpressTokenType.RightParentheses: + case STJsonPathExpressionTokenType.RightParentheses: while (true) { if (stack_sop.Count == 0) { throw new STJsonPathParseException(token.Index, "Invalid express [" + ME.GetExpressText(tokens) + "]"); } sop_top = stack_sop.Pop(); - if (sop_top.Type == ExpressTokenType.LeftParentheses) { + if (sop_top.Type == STJsonPathExpressionTokenType.LeftParentheses) { break; } if (lst_out.Count < 2) { @@ -263,11 +263,11 @@ public static List GetExpressPolishQueue(List lst_out.Add(sop_top); } continue; - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: str_last_op = token.Value.ToString(); while (stack_sop.Count > 0) { sop_top = stack_sop.First(); - if (sop_top.Type == ExpressTokenType.LeftParentheses) { + if (sop_top.Type == STJsonPathExpressionTokenType.LeftParentheses) { break; } if (m_dic_op_priority[token.Value.ToString()] > m_dic_op_priority[sop_top.Value.ToString()]) { @@ -286,10 +286,10 @@ public static List GetExpressPolishQueue(List return lst_out; } - private static void CheckExpress(List tokens) { + private static void CheckExpress(List tokens) { switch (tokens[0].Type) { - case ExpressTokenType.RightParentheses: - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.RightParentheses: + case STJsonPathExpressionTokenType.Operator: switch (tokens[0].Value) { case "!": case "~": @@ -299,7 +299,7 @@ private static void CheckExpress(List tokens) { } break; } - if (tokens[tokens.Count - 1].Type == ExpressTokenType.Operator) { + if (tokens[tokens.Count - 1].Type == STJsonPathExpressionTokenType.Operator) { throw new STJsonPathParseException(tokens[tokens.Count - 1].Index, "Invalid expression {" + ME.GetExpressText(tokens) + "}"); } double d_v = 0; @@ -307,29 +307,29 @@ private static void CheckExpress(List tokens) { for (int i = 0; i < tokens.Count; i++) { var token = tokens[i]; switch (token.Type) { - case ExpressTokenType.Long: + case STJsonPathExpressionTokenType.Long: l_v = Convert.ToInt64(token.Value); - if (l_v < 0 && i != 0 && tokens[i - 1].Type != ExpressTokenType.Operator) { + if (l_v < 0 && i != 0 && tokens[i - 1].Type != STJsonPathExpressionTokenType.Operator) { token.Value = -l_v; tokens[i] = token; - tokens.Insert(i, ExpressionToken.Create(-1, ExpressTokenType.Operator, "-")); + tokens.Insert(i, STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Operator, "-")); i++; } break; - case ExpressTokenType.Double: + case STJsonPathExpressionTokenType.Double: d_v = Convert.ToDouble(token.Value); - if (d_v < 0 && i != 0 && tokens[i - 1].Type != ExpressTokenType.Operator) { + if (d_v < 0 && i != 0 && tokens[i - 1].Type != STJsonPathExpressionTokenType.Operator) { token.Value = -d_v; tokens[i] = token; - tokens.Insert(i, ExpressionToken.Create(-1, ExpressTokenType.Operator, "-")); + tokens.Insert(i, STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Operator, "-")); i++; } break; - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: switch (token.Value.ToString()) { case "!": case "~": - tokens.Insert(i, ExpressionToken.Create(-1, ExpressTokenType.Long, 0)); + tokens.Insert(i, STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, 0)); i++; break; } @@ -338,26 +338,26 @@ private static void CheckExpress(List tokens) { } int nA = 0; bool bNeedOP = false; - var token_last = new ExpressionToken(); + var token_last = new STJsonPathExpressionToken(); for (int i = 0; i < tokens.Count; i++) { var token = tokens[i]; switch (token.Type) { - case ExpressTokenType.LeftParentheses: + case STJsonPathExpressionTokenType.LeftParentheses: switch (token_last.Type) { - case ExpressTokenType.Undefined: - case ExpressTokenType.LeftParentheses: - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Undefined: + case STJsonPathExpressionTokenType.LeftParentheses: + case STJsonPathExpressionTokenType.Operator: break; throw new STJsonPathParseException(token.Index, "Invalid expression {" + ME.GetExpressText(tokens) + "}"); } nA++; bNeedOP = false; break; - case ExpressTokenType.RightParentheses: + case STJsonPathExpressionTokenType.RightParentheses: switch (token_last.Type) { - case ExpressTokenType.RightParentheses: + case STJsonPathExpressionTokenType.RightParentheses: break; - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: throw new STJsonPathParseException(token.Index, "Invalid expression {" + ME.GetExpressText(tokens) + "}"); } nA--; @@ -366,7 +366,7 @@ private static void CheckExpress(List tokens) { } bNeedOP = true; break; - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: if (!bNeedOP) { throw new STJsonPathParseException(token.Index, "Invalid expression {" + ME.GetExpressText(tokens) + "}"); } @@ -384,7 +384,7 @@ private static void CheckExpress(List tokens) { } - private static string GetExpressText(List tokens) { + private static string GetExpressText(List tokens) { string strText = string.Empty; foreach (var v in tokens) { strText += v.Text; diff --git a/Src/STLib.Json/JsonPathParser/STJsonPathExpression.cs b/Src/STLib.Json/STJsonPath/STJsonPathExpression.cs similarity index 55% rename from Src/STLib.Json/JsonPathParser/STJsonPathExpression.cs rename to Src/STLib.Json/STJsonPath/STJsonPathExpression.cs index 210b685..7db27a6 100644 --- a/Src/STLib.Json/JsonPathParser/STJsonPathExpression.cs +++ b/Src/STLib.Json/STJsonPath/STJsonPathExpression.cs @@ -10,19 +10,22 @@ internal class STJsonPathExpression { public string Text { get; private set; } public bool IsFilter { get; set; } - public List Express { get; set; } + public List Express { get; set; } - public STJsonPathExpression(string strText, List lstExpTokens, bool isFilter) { + public STJsonPathExpression(string strText, List lstExpTokens, bool isFilter) + { this.Text = strText; this.Express = lstExpTokens; this.IsFilter = isFilter; } - public ExpressionToken Excute(STJson jsonRoot, STJson jsonCurrent) { + public STJsonPathExpressionToken Excute(STJson jsonRoot, STJson jsonCurrent) + { return ME.Excute(this.Express, jsonRoot, jsonCurrent); } - public ExpressionToken ExcuteTest(STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) { + public STJsonPathExpressionToken ExcuteTest(STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) + { jsonOut.SetItem("type", "expression"); jsonOut.SetItem("parsed", this.Text); var jsonPolish = jsonOut.SetKey("polish"); @@ -31,18 +34,18 @@ public ExpressionToken ExcuteTest(STJson jsonRoot, STJson jsonCurrent, STJson js } var token = ME.ExcuteTest(this.Express, jsonRoot, jsonCurrent, jsonOut); jsonOut.SetItem("return", new STJson() - .SetItem("bool", !(token.Type == ExpressTokenType.Undefined || token.Value == null || token.Value.Equals(false))) .SetItem("value_type", token.Type.ToString()) + .SetItem("bool_value", !(token.Type == STJsonPathExpressionTokenType.Undefined || token.Type == STJsonPathExpressionTokenType.Error || token.Value == null || token.Value.Equals(false))) ); switch (token.Type) { - case ExpressTokenType.Array: - jsonOut["return"].SetItem("items", STJson.CreateArray()); - foreach (var v in token.Value as ExpressionToken[]) { - jsonOut["return"]["items"].Append(v.Text); + case STJsonPathExpressionTokenType.Array: + jsonOut["return"].SetItem("value", STJson.CreateArray()); + foreach (var v in token.Value as STJsonPathExpressionToken[]) { + jsonOut["return"]["value"].Append(v.Text); } break; default: - jsonOut["return"].SetItem("text", token.Text); + jsonOut["return"].SetItem("value", token.Text); break; } return token; @@ -50,20 +53,23 @@ public ExpressionToken ExcuteTest(STJson jsonRoot, STJson jsonCurrent, STJson js // ===================================================================================================== - private static ExpressionToken Excute(List tokens, STJson jsonRoot, STJson jsonCurrent) { - ExpressionToken token_l = ExpressionToken.Undefined; - ExpressionToken token_r = ExpressionToken.Undefined; - Stack stack_result = new Stack(); + private static STJsonPathExpressionToken Excute(List tokens, STJson jsonRoot, STJson jsonCurrent) + { + STJsonPathExpressionToken token_l = STJsonPathExpressionToken.Undefined; + STJsonPathExpressionToken token_r = STJsonPathExpressionToken.Undefined; + Stack stack_result = new Stack(); foreach (var obj in tokens) { switch (obj.Type) { - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: token_r = stack_result.Pop(); token_l = stack_result.Pop(); token_l = ME.GetExpressToken(token_l, jsonRoot, jsonCurrent); token_r = ME.GetExpressToken(token_r, jsonRoot, jsonCurrent); - if (token_l.Type == ExpressTokenType.Undefined || token_r.Type == ExpressTokenType.Undefined) { - return ExpressionToken.Undefined; - } + if (token_l.Type == STJsonPathExpressionTokenType.Error) return token_l; + if (token_r.Type == STJsonPathExpressionTokenType.Error) return token_r; + //if (token_l.Type == ExpressTokenType.Undefined || token_r.Type == ExpressTokenType.Undefined) { + // return ExpressionToken.Undefined; + //} switch (obj.Value) { case "+": case "-": @@ -105,170 +111,176 @@ private static ExpressionToken Excute(List tokens, STJson jsonR } } - if (stack_result.Count != 1) return ExpressionToken.Undefined; + if (stack_result.Count != 1) return STJsonPathExpressionToken.Undefined; var ret = stack_result.Pop(); return ME.GetExpressToken(ret, jsonRoot, jsonCurrent); } - private static ExpressionToken GetExpressToken(ExpressionToken token, STJson jsonRoot, STJson jsonCurrent) { + private static STJsonPathExpressionToken GetExpressToken(STJsonPathExpressionToken token, STJson jsonRoot, STJson jsonCurrent) + { switch (token.Type) { - case ExpressTokenType.PathItem: + case STJsonPathExpressionTokenType.PathItem: token = ME.GetExpressTokenFromPathItems(token, jsonRoot, jsonCurrent); break; - case ExpressTokenType.Func: + case STJsonPathExpressionTokenType.Func: token = STJsonPathExpression.ExcuteFunc(token, jsonRoot, jsonCurrent); break; - case ExpressTokenType.ArrayExp: + case STJsonPathExpressionTokenType.ArrayExp: token = ME.ExcuteArray(token, jsonRoot, jsonCurrent); break; } return token; } - private static ExpressionToken GetExpressTokenFromPathItems(ExpressionToken token, STJson jsonRoot, STJson jsonCurrent) { + private static STJsonPathExpressionToken GetExpressTokenFromPathItems(STJsonPathExpressionToken token, STJson jsonRoot, STJson jsonCurrent) + { var json_result = STJson.CreateArray(); - SelectSetting setting = new SelectSetting() { + SelectSetting setting = new SelectSetting() + { Root = jsonRoot, Path = new Stack(), Mode = STJsonPathSelectMode.ItemOnly }; STJsonPath.GetSTJsons(token.PathItems, 0, jsonCurrent, setting, json_result); if (json_result.Count == 0) { - return ExpressionToken.Undefined; + return STJsonPathExpressionToken.Undefined; } return ME.GetExpressTokenFromSTJson(json_result[0]); } - private static ExpressionToken GetExpressTokenFromSTJson(STJson json) { - ExpressionToken[] arr_obj = null; - if (json == null || json.IsNullObject) { - return ExpressionToken.Create(-1, ExpressTokenType.Object, null); + private static STJsonPathExpressionToken GetExpressTokenFromSTJson(STJson json) + { + STJsonPathExpressionToken[] arr_obj = null; + if (json == null || json.IsNullValue) { + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Object, null); } switch (json.ValueType) { case STJsonValueType.Long: - return ExpressionToken.Create(-1, ExpressTokenType.Long, json.Value); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Long, json.Value); case STJsonValueType.Double: - return ExpressionToken.Create(-1, ExpressTokenType.Double, json.Value); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Double, json.Value); case STJsonValueType.String: - return ExpressionToken.Create(-1, ExpressTokenType.String, json.Value); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, json.Value); case STJsonValueType.Boolean: - return ExpressionToken.Create(-1, ExpressTokenType.Boolean, json.Value); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Boolean, json.Value); case STJsonValueType.Datetime: - return ExpressionToken.Create(-1, ExpressTokenType.String, ((DateTime)json.Value).ToString("O")); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.String, ((DateTime)json.Value).ToString("O")); case STJsonValueType.Array: - arr_obj = new ExpressionToken[json.Count]; + arr_obj = new STJsonPathExpressionToken[json.Count]; for (int i = 0; i < json.Count; i++) { arr_obj[i] = ME.GetExpressTokenFromSTJson(json[i]); } - return ExpressionToken.Create(-1, ExpressTokenType.Array, arr_obj); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Array, arr_obj); } - return ExpressionToken.Create(-1, ExpressTokenType.Object, json); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Object, json); } - private static ExpressionToken ExcuteArray(ExpressionToken token, STJson jsonRoot, STJson jsonCurrent) { + private static STJsonPathExpressionToken ExcuteArray(STJsonPathExpressionToken token, STJson jsonRoot, STJson jsonCurrent) + { STJsonPathExpression[] src = token.Value as STJsonPathExpression[]; - ExpressionToken[] result = new ExpressionToken[src.Length]; + STJsonPathExpressionToken[] result = new STJsonPathExpressionToken[src.Length]; for (int i = 0; i < src.Length; i++) { result[i] = src[i].Excute(jsonRoot, jsonCurrent); } - return ExpressionToken.Create(-1, ExpressTokenType.Array, result); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Array, result); } - private static ExpressionToken ExcuteFunc(ExpressionToken exp, STJson jsonRoot, STJson jsonCurrent) { - var arr_exp_arg = new ExpressionToken[exp.Args.Count]; + private static STJsonPathExpressionToken ExcuteFunc(STJsonPathExpressionToken exp, STJson jsonRoot, STJson jsonCurrent) + { + var arr_exp_arg = new STJsonPathExpressionToken[exp.Args.Count]; for (int i = 0; i < exp.Args.Count; i++) { arr_exp_arg[i] = exp.Args[i].Excute(jsonRoot, jsonCurrent); } - string strFuncName = exp.Value.ToString(); - if (STJsonPath.CustomFunctions.ContainsKey(strFuncName)) { - var arr_custom_arg = new STJsonPathExpFuncArg[exp.Args.Count]; + string str_func_name = exp.Value.ToString(); + if (STJsonPath.CustomFunctions.ContainsKey(str_func_name)) { + var arr_custom_arg = new STJsonPathExpressionFunctionArg[exp.Args.Count]; for (int i = 0; i < arr_exp_arg.Length; i++) { arr_custom_arg[i] = ME.ConvertExpressTokenToFuncArg(arr_exp_arg[i]); } - var json = STJsonPath.CustomFunctions[strFuncName](arr_custom_arg); + var json = STJsonPath.CustomFunctions[str_func_name](arr_custom_arg); if (exp.PathItems != null && exp.PathItems.Count != 0) { return ME.GetExpressTokenFromPathItems(exp, json, json); } return ME.GetExpressTokenFromSTJson(json); } - if (STJsonPath.BuildInFunctions.ContainsKey(strFuncName)) { - return STJsonPath.BuildInFunctions[strFuncName](arr_exp_arg); + if (STJsonPath.BuildInFunctions.ContainsKey(str_func_name)) { + return STJsonPath.BuildInFunctions[str_func_name](arr_exp_arg); } - return ExpressionToken.Undefined; - //throw new STJsonPathExpressException(1, "Can not found the function [" + strFuncName + "]"); + return STJsonPathExpressionToken.CreateError("Can not match the function: " + str_func_name + "(" + STJsonPathBuildInFunctions.GetFunctionArgTypeList(arr_exp_arg) + "). "); } - private static STJsonPathExpFuncArg ConvertExpressTokenToFuncArg(ExpressionToken token) { - STJsonPathExpFuncArg arg = new STJsonPathExpFuncArg(); - if (token.Type == ExpressTokenType.Array) { - var tokens = token.Value as ExpressionToken[]; - STJsonPathExpFuncArg[] arr_arg = new STJsonPathExpFuncArg[tokens.Length]; - arg.Type = STJsonPathExpFuncArgType.Array; + private static STJsonPathExpressionFunctionArg ConvertExpressTokenToFuncArg(STJsonPathExpressionToken token) + { + STJsonPathExpressionFunctionArg arg = new STJsonPathExpressionFunctionArg(); + if (token.Type == STJsonPathExpressionTokenType.Array) { + var tokens = token.Value as STJsonPathExpressionToken[]; + STJsonPathExpressionFunctionArg[] arr_arg = new STJsonPathExpressionFunctionArg[tokens.Length]; + arg.Type = STJsonPathExpressionFunctionArgType.Array; for (int i = 0; i < arr_arg.Length; i++) { arr_arg[i] = ME.ConvertExpressTokenToFuncArg(tokens[i]); } arg.Value = arr_arg; return arg; } - if (token.Type <= ExpressTokenType.Object) { - arg.Type = (STJsonPathExpFuncArgType)token.Type; + if (token.Type <= STJsonPathExpressionTokenType.Object) { + arg.Type = (STJsonPathExpressionFunctionArgType)token.Type; arg.Value = token.Value; } return arg; } - private static ExpressionToken ExcuteBasic(ExpressionToken l, ExpressionToken r, string strOP) { - if (l.Type == ExpressTokenType.String || r.Type == ExpressTokenType.String) { + private static STJsonPathExpressionToken ExcuteBasic(STJsonPathExpressionToken l, STJsonPathExpressionToken r, string strOP) + { + if (l.Type == STJsonPathExpressionTokenType.String || r.Type == STJsonPathExpressionTokenType.String) { if (strOP == "+") { - return ExpressionToken.Create( + return STJsonPathExpressionToken.Create( -1, - ExpressTokenType.String, + STJsonPathExpressionTokenType.String, Convert.ToString(l.Value) + Convert.ToString(r.Value) ); } - return ExpressionToken.Undefined; - //throw new STJsonPathExpressException(0, "The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); + return STJsonPathExpressionToken.CreateError("The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); } - ExpressionToken token_ret = new ExpressionToken() { - Index = -1, - Type = (l.Type == ExpressTokenType.Double || r.Type == ExpressTokenType.Double) - ? ExpressTokenType.Double - : ExpressTokenType.Long - }; - if (token_ret.Type == ExpressTokenType.Double) { - double d_l = Convert.ToDouble(l.Value); - double d_r = Convert.ToDouble(r.Value); - switch (strOP) { - case "+": token_ret.Value = d_l + d_r; break; - case "-": token_ret.Value = d_l - d_r; break; - case "*": token_ret.Value = d_l * d_r; break; - case "/": token_ret.Value = d_l / d_r; break; - case "%": - return ExpressionToken.Undefined; - //throw new STJsonPathExpressException(0, "The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); - } - } else { - long l_l = Convert.ToInt64(l.Value); - long l_r = Convert.ToInt64(r.Value); - switch (strOP) { - case "+": token_ret.Value = l_l + l_r; break; - case "-": token_ret.Value = l_l - l_r; break; - case "*": token_ret.Value = l_l * l_r; break; - case "/": token_ret.Value = l_l / l_r; break; - case "%": token_ret.Value = l_l % l_r; break; + if (l.IsNumber && r.IsNumber) { + var token_ret = new STJsonPathExpressionToken() { Index = -1 };// ExpressionToken.Create(-1, ExpressTokenType.Double, 0); + if (l.Type == STJsonPathExpressionTokenType.Double || r.Type == STJsonPathExpressionTokenType.Double) { + token_ret.Type = STJsonPathExpressionTokenType.Double; + double d_l = Convert.ToDouble(l.Value); + double d_r = Convert.ToDouble(r.Value); + switch (strOP) { + case "+": token_ret.Value = d_l + d_r; break; + case "-": token_ret.Value = d_l - d_r; break; + case "*": token_ret.Value = d_l * d_r; break; + case "/": token_ret.Value = d_l / d_r; break; + case "%": + return STJsonPathExpressionToken.CreateError("The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); + } + } else { + token_ret.Type = STJsonPathExpressionTokenType.Long; + long l_l = Convert.ToInt64(l.Value); + long l_r = Convert.ToInt64(r.Value); + switch (strOP) { + case "+": token_ret.Value = l_l + l_r; break; + case "-": token_ret.Value = l_l - l_r; break; + case "*": token_ret.Value = l_l * l_r; break; + case "/": token_ret.Value = l_l / l_r; break; + case "%": token_ret.Value = l_l % l_r; break; + } } + return token_ret; } - return token_ret; + return STJsonPathExpressionToken.CreateError("The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); } - private static ExpressionToken ExcuteBit(ExpressionToken l, ExpressionToken r, string strOP) { - if (l.Type != ExpressTokenType.Long || r.Type != ExpressTokenType.Long) { - return ExpressionToken.Undefined; - //throw new STJsonPathExpressException(0, "The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); + private static STJsonPathExpressionToken ExcuteBit(STJsonPathExpressionToken l, STJsonPathExpressionToken r, string strOP) + { + if (l.Type != STJsonPathExpressionTokenType.Long || r.Type != STJsonPathExpressionTokenType.Long) { + return STJsonPathExpressionToken.CreateError("The data types do not match and this operation cannot be completed. {" + l.Type + " " + strOP + " " + r.Type + "}"); } - ExpressionToken token_ret = new ExpressionToken() { - Type = ExpressTokenType.Long + STJsonPathExpressionToken token_ret = new STJsonPathExpressionToken() + { + Type = STJsonPathExpressionTokenType.Long }; long l_l = Convert.ToInt64(l.Value); long l_r = Convert.ToInt64(r.Value); @@ -283,10 +295,12 @@ private static ExpressionToken ExcuteBit(ExpressionToken l, ExpressionToken r, s return token_ret; } - private static ExpressionToken ExcuteLogic(ExpressionToken l, ExpressionToken r, string strOP) { - ExpressionToken token_ret = new ExpressionToken() { + private static STJsonPathExpressionToken ExcuteLogic(STJsonPathExpressionToken l, STJsonPathExpressionToken r, string strOP) + { + STJsonPathExpressionToken token_ret = new STJsonPathExpressionToken() + { Index = -1, - Type = ExpressTokenType.Boolean + Type = STJsonPathExpressionTokenType.Boolean }; switch (strOP) { case "!": token_ret.Value = !Convert.ToBoolean(r.Value); return token_ret; @@ -321,9 +335,9 @@ private static ExpressionToken ExcuteLogic(ExpressionToken l, ExpressionToken r, return token_ret; } if (!l.IsNumber || !r.IsNumber) { - return ExpressionToken.False; + return STJsonPathExpressionToken.False; } - if (l.Type == ExpressTokenType.Double || r.Type == ExpressTokenType.Double) { + if (l.Type == STJsonPathExpressionTokenType.Double || r.Type == STJsonPathExpressionTokenType.Double) { var d_l = Convert.ToDouble(l.Value); var d_r = Convert.ToDouble(r.Value); switch (strOP) { @@ -345,16 +359,17 @@ private static ExpressionToken ExcuteLogic(ExpressionToken l, ExpressionToken r, return token_ret; } - private static ExpressionToken ExcuteList(ExpressionToken l, ExpressionToken r, string strOP) { - if (r.Type != ExpressTokenType.Array) { - return ExpressionToken.False; + private static STJsonPathExpressionToken ExcuteList(STJsonPathExpressionToken l, STJsonPathExpressionToken r, string strOP) + { + if (r.Type != STJsonPathExpressionTokenType.Array) { + return STJsonPathExpressionToken.False; } - ExpressionToken[] arr_l = null; - ExpressionToken[] arr_r = r.Value as ExpressionToken[]; - if (l.Type == ExpressTokenType.Array) { - arr_l = l.Value as ExpressionToken[]; + STJsonPathExpressionToken[] arr_l = null; + STJsonPathExpressionToken[] arr_r = r.Value as STJsonPathExpressionToken[]; + if (l.Type == STJsonPathExpressionTokenType.Array) { + arr_l = l.Value as STJsonPathExpressionToken[]; } else { - arr_l = new ExpressionToken[] { l }; + arr_l = new STJsonPathExpressionToken[] { l }; } int nCounter = 0; switch (strOP) { @@ -367,42 +382,43 @@ private static ExpressionToken ExcuteList(ExpressionToken l, ExpressionToken r, } } } - return nCounter == arr_l.Length ? ExpressionToken.True : ExpressionToken.False; + return nCounter == arr_l.Length ? STJsonPathExpressionToken.True : STJsonPathExpressionToken.False; case "nin": foreach (var obj_l in arr_l) { foreach (var obj_r in arr_r) { if (object.Equals(obj_l.Value, obj_r.Value)) { - return ExpressionToken.False; + return STJsonPathExpressionToken.False; } } } - return ExpressionToken.True; + return STJsonPathExpressionToken.True; case "anyof": foreach (var obj_l in arr_l) { foreach (var obj_r in arr_r) { if (object.Equals(obj_l.Value, obj_r.Value)) { - return ExpressionToken.True; + return STJsonPathExpressionToken.True; } } } - return ExpressionToken.False; + return STJsonPathExpressionToken.False; } - return ExpressionToken.False; + return STJsonPathExpressionToken.False; } // ===================================================================================================== - private static ExpressionToken ExcuteTest(List tokens, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) { - ExpressionToken token_l = ExpressionToken.Undefined; - ExpressionToken token_r = ExpressionToken.Undefined; - ExpressionToken token_result = ExpressionToken.Undefined; - Stack stack_result = new Stack(); + private static STJsonPathExpressionToken ExcuteTest(List tokens, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) + { + STJsonPathExpressionToken token_l = STJsonPathExpressionToken.Undefined; + STJsonPathExpressionToken token_r = STJsonPathExpressionToken.Undefined; + STJsonPathExpressionToken token_result = STJsonPathExpressionToken.Undefined; + Stack stack_result = new Stack(); STJson jsonStep = null; STJson jsonSteps = STJson.CreateArray(); jsonOut.SetItem("steps", jsonSteps); foreach (var obj in tokens) { switch (obj.Type) { - case ExpressTokenType.Operator: + case STJsonPathExpressionTokenType.Operator: jsonStep = new STJson(); jsonStep.SetItem("type", "excute"); jsonStep.SetItem("operator", obj.Value); @@ -411,9 +427,11 @@ private static ExpressionToken ExcuteTest(List tokens, STJson j token_l = stack_result.Pop(); token_l = ME.GetExpressTokenTest(token_l, jsonRoot, jsonCurrent, jsonStep.SetKey("get_left_token")); token_r = ME.GetExpressTokenTest(token_r, jsonRoot, jsonCurrent, jsonStep.SetKey("get_right_token")); - if (token_l.Type == ExpressTokenType.Undefined || token_r.Type == ExpressTokenType.Undefined) { - return ExpressionToken.Undefined; - } + if (token_l.Type == STJsonPathExpressionTokenType.Error) return token_l; + if (token_r.Type == STJsonPathExpressionTokenType.Error) return token_r; + //if (token_l.Type == STJsonPathExpressionTokenType.Undefined || token_r.Type == STJsonPathExpressionTokenType.Undefined) { + // return STJsonPathExpressionToken.Undefined; + //} switch (obj.Value) { case "+": case "-": @@ -459,39 +477,46 @@ private static ExpressionToken ExcuteTest(List tokens, STJson j } if (stack_result.Count != 1) { jsonOut.SetItem("error", "Empty expression."); - return ExpressionToken.Undefined; + return STJsonPathExpressionToken.Undefined; } var ret = stack_result.Pop(); return ME.GetExpressTokenTest(ret, jsonRoot, jsonCurrent, jsonOut.SetKey("check_result")); } - private static ExpressionToken GetExpressTokenTest(ExpressionToken token, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) { - jsonOut.SetItem("parsed", token.Text); + private static STJsonPathExpressionToken GetExpressTokenTest(STJsonPathExpressionToken token, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) + { switch (token.Type) { - case ExpressTokenType.PathItem: + case STJsonPathExpressionTokenType.PathItem: jsonOut.SetItem("type", "selector"); token = ME.GetExpressTokenFromPathItemsTest(token, jsonRoot, jsonCurrent, jsonOut); break; - case ExpressTokenType.Func: + case STJsonPathExpressionTokenType.Func: jsonOut.SetItem("type", "function"); token = STJsonPathExpression.ExcuteFuncTest(token, jsonRoot, jsonCurrent, jsonOut); break; - case ExpressTokenType.ArrayExp: + case STJsonPathExpressionTokenType.ArrayExp: jsonOut.SetItem("type", "array_expression"); token = ME.ExcuteArrayTest(token, jsonRoot, jsonCurrent, jsonOut); break; + case STJsonPathExpressionTokenType.Error: + jsonOut.SetItem("type", "error"); + break; default: jsonOut.SetItem("type", "value"); break; } + jsonOut.SetItem("parsed", token.Text); jsonOut.SetItem("result", new STJson().SetItem("value_type", token.Type.ToString()).SetItem("text", token.Text)); return token; } - private static ExpressionToken GetExpressTokenFromPathItemsTest(ExpressionToken token, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) { + private static STJsonPathExpressionToken GetExpressTokenFromPathItemsTest(STJsonPathExpressionToken token, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) + { var json_result = STJson.CreateArray(); - SelectSetting setting = new SelectSetting() { + SelectSetting setting = new SelectSetting() + { Root = jsonRoot, + Path = new Stack(), Mode = STJsonPathSelectMode.ItemOnly }; STJsonPath.GetSTJsons(token.PathItems, 0, jsonCurrent, setting, json_result); @@ -499,31 +524,33 @@ private static ExpressionToken GetExpressTokenFromPathItemsTest(ExpressionToken jsonOut.SetItem("current_json", jsonCurrent == null ? null : jsonCurrent.ToString()); jsonOut.SetItem("selected_json", json_result.ToString()); if (json_result.Count == 0) { - return ExpressionToken.Undefined; + return STJsonPathExpressionToken.Undefined; } var token_ret = ME.GetExpressTokenFromSTJson(json_result[0]); return token_ret; } - private static ExpressionToken ExcuteArrayTest(ExpressionToken token, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) { + private static STJsonPathExpressionToken ExcuteArrayTest(STJsonPathExpressionToken token, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) + { var jsonItems = STJson.CreateArray(); jsonOut.SetItem("get_items", jsonItems); STJsonPathExpression[] src = token.Value as STJsonPathExpression[]; - ExpressionToken[] result = new ExpressionToken[src.Length]; + STJsonPathExpressionToken[] result = new STJsonPathExpressionToken[src.Length]; for (int i = 0; i < src.Length; i++) { var jsonItem = new STJson(); result[i] = src[i].ExcuteTest(jsonRoot, jsonCurrent, jsonItem); jsonItems.Append(jsonItem); } - return ExpressionToken.Create(-1, ExpressTokenType.Array, result); + return STJsonPathExpressionToken.Create(-1, STJsonPathExpressionTokenType.Array, result); } - private static ExpressionToken ExcuteFuncTest(ExpressionToken exp, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) { - var token_return = ExpressionToken.Undefined; + private static STJsonPathExpressionToken ExcuteFuncTest(STJsonPathExpressionToken exp, STJson jsonRoot, STJson jsonCurrent, STJson jsonOut) + { + var token_return = STJsonPathExpressionToken.Undefined; var jsonArgs = STJson.CreateArray(); List lst_args = new List(); jsonOut.SetItem("get_args", jsonArgs); - var arr_exp_arg = new ExpressionToken[exp.Args.Count]; + var arr_exp_arg = new STJsonPathExpressionToken[exp.Args.Count]; for (int i = 0; i < exp.Args.Count; i++) { var jsonArg = new STJson(); arr_exp_arg[i] = exp.Args[i].ExcuteTest(jsonRoot, jsonCurrent, jsonArg); @@ -531,14 +558,14 @@ private static ExpressionToken ExcuteFuncTest(ExpressionToken exp, STJson jsonRo lst_args.Add(arr_exp_arg[i].Text); } jsonOut.SetItem("args_text", lst_args); - string strFuncName = exp.Value.ToString(); - if (STJsonPath.CustomFunctions.ContainsKey(strFuncName)) { + string str_func_name = exp.Value.ToString(); + if (STJsonPath.CustomFunctions.ContainsKey(str_func_name)) { jsonOut.SetItem("type", "custom_function"); - var arr_custom_arg = new STJsonPathExpFuncArg[exp.Args.Count]; + var arr_custom_arg = new STJsonPathExpressionFunctionArg[exp.Args.Count]; for (int i = 0; i < arr_exp_arg.Length; i++) { arr_custom_arg[i] = ME.ConvertExpressTokenToFuncArg(arr_exp_arg[i]); } - var json = STJsonPath.CustomFunctions[strFuncName](arr_custom_arg); + var json = STJsonPath.CustomFunctions[str_func_name](arr_custom_arg); jsonOut.SetItem("result_json", json.ToString()); if (exp.PathItems != null && exp.PathItems.Count != 0) { token_return = ME.GetExpressTokenFromPathItemsTest(exp, json, json, jsonOut); @@ -547,12 +574,12 @@ private static ExpressionToken ExcuteFuncTest(ExpressionToken exp, STJson jsonRo } return token_return; } - if (STJsonPath.BuildInFunctions.ContainsKey(strFuncName)) { + if (STJsonPath.BuildInFunctions.ContainsKey(str_func_name)) { jsonOut.SetItem("type", "buildin_function"); - return STJsonPath.BuildInFunctions[strFuncName](arr_exp_arg); + return STJsonPath.BuildInFunctions[str_func_name](arr_exp_arg); } - jsonOut.SetItem("error", "Can not found the function [" + strFuncName + "]"); - return ExpressionToken.Undefined; + jsonOut.SetItem("error", "Can not match the function: " + str_func_name + "(" + STJsonPathBuildInFunctions.GetFunctionArgTypeList(arr_exp_arg) + ")."); + return STJsonPathExpressionToken.CreateError("Can not match the function: " + str_func_name + "(" + STJsonPathBuildInFunctions.GetFunctionArgTypeList(arr_exp_arg) + ")."); } } } diff --git a/Src/STLib.Json/JsonPathParser/STJsonPathItem.cs b/Src/STLib.Json/STJsonPath/STJsonPathItem.cs similarity index 100% rename from Src/STLib.Json/JsonPathParser/STJsonPathItem.cs rename to Src/STLib.Json/STJsonPath/STJsonPathItem.cs diff --git a/Src/STLib.Json/JsonPathParser/STJsonPathParser.cs b/Src/STLib.Json/STJsonPath/STJsonPathParser.cs similarity index 100% rename from Src/STLib.Json/JsonPathParser/STJsonPathParser.cs rename to Src/STLib.Json/STJsonPath/STJsonPathParser.cs diff --git a/Src/STLib.Json/JsonPathParser/STJsonPathTokenizer.cs b/Src/STLib.Json/STJsonPath/STJsonPathTokenizer.cs similarity index 100% rename from Src/STLib.Json/JsonPathParser/STJsonPathTokenizer.cs rename to Src/STLib.Json/STJsonPath/STJsonPathTokenizer.cs diff --git a/Src/STLib.Json/STLib.Json.csproj b/Src/STLib.Json/STLib.Json.csproj index be1cd40..f1484b8 100644 --- a/Src/STLib.Json/STLib.Json.csproj +++ b/Src/STLib.Json/STLib.Json.csproj @@ -1,7 +1,27 @@  - net6.0;netstandard2.0;net35;net46;net47;net48 + unknows + 2017 + 2019 + 2022 + + + + net40 + + + net35 + + + netstandard2.0;net46; + + + net6.0;net8.0;netstandard2.0;net35;net46;net47;net48 + + + + STJson True DebugST @@ -14,23 +34,45 @@ STJson is a Json parsing library based on the MIT open source protocol . The library is purely native implementation does not rely on any library , so it is very light and convenient , and powerful . True git - ------------[2023-11-27]------------ + [3.0.0][2024-08-02] +-------------------------------------------------- +Add Json5 support. +Add STJsonReader/Writer. +Add STJsonCreator. +Add STJson.ToString(TextWriter). +Optimize STJson.Sort(). +Optimize some STJsonPath built-in functions. + +[2.0.0][2024-02-14] +-------------------------------------------------- +Fixed JsonPath error for [?()] +Fixed some string serialization cannot be parsed in other languages. + +[1.0.4][2023-11-27] +-------------------------------------------------- Fixed parsing string error for ["....\\"] Fixed parsing number error for [12E-12] -------------[2023-11-10]------------ +Changed STJsonPathCallBack + +[1.0.3][2023-11-10] +-------------------------------------------------- Fixed DateTime type sorting error. Fixed parsing scientific notation string error. Fixed STJson.GetValue<object>() error. -------------[2023-07-24]------------ + +[1.0.1][2023-07-24] +-------------------------------------------------- Fixed STJson.GetValue<T>() error. -------------[2023-06-04]------------ + +[1.0.0][2023-06-04] +-------------------------------------------------- Publish project. Support Json serialization and deserialization. Support JsonPath syntax. Simple data aggregation processing. Copyright 2022 DebugST LICENSE - 1.0.5 + 3.0.0 diff --git a/docs/index.html b/docs/index.html index eed1c75..93f0a0f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,7 +31,7 @@

STLib.Json

一款轻量级Json操作库,功能强大且使用简单。Emm...反正就是很强大。。。很简单。。。

在线教程GitHub -

版本:1.0

+

版本:3.0

@@ -50,7 +50,7 @@

STLib.Json

哇喔。。。快看!石头好TM帅!!!

-

2023-06-08

+

2024-08-02

@@ -62,7 +62,7 @@

STLib.Json

A lightweight Json manipulation library that is powerful and easy to use. Emm...Anyway...it's p...p...powerful. v...v...very simple.

TutorialGitHub -

Version:1.0

+

Version:3.0

@@ -81,7 +81,7 @@

STLib.Json

Wa...Oh...Look!DebugST is so cool!!!

-

2023-06-08

+

2024-08-02

diff --git a/docs/tutorial_cn.html b/docs/tutorial_cn.html index faad546..da84f9a 100644 --- a/docs/tutorial_cn.html +++ b/docs/tutorial_cn.html @@ -1,2018 +1,2291 @@ - - - - - -STJson - - - - - - - -
- - -
-

STLib.STJson

-

简介

-

STLib.STJson是一款基于MIT开源协议的Json解析库。由DebugST开发,该库纯原生实现不依赖任何库,所以非常轻量便捷,且功能强大。由于没用任何依赖纯原生构建,它可以很容易移植到其他编程语言中。当然由于作者懒的一批,这个概率不是很大。

-

在设计STJson的时候作者没有任何的异议,毕竟Json的数据格式不存在有争议的地方,当在设计STJsonPath的时候,作者犹豫了,因为作者发现很多存在争议的地方,且无法找到关于JsonPath的正式RFC文档,仅仅找到一份草案:

-

JSONPath: Query expressions for JSON

-

虽然草案中一些疑惑也能得到解决,但是作者仍有一些疑惑,毕竟不是正式的文档。在开发之前作者也好奇其他JsonPath的开源库,对于有争议的地方其他库是如何解决的。但很遗憾,问题依然存在。似乎没有库愿意搭理这份草案。既然JsonPath还没有正式的RFC文档,那么作者也想自立门户。但作者并不会不搭理这份草案,只能说作者会尽可能兼容草案的同时,还会加入一些作者自己的想法。

-

路劲疑问

-

JsonPath是源自于XPath的使用方式,总所周知XPathXML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。显而易见JsonPath是用于Json的。它们两种几乎使用方式相同,但是XMLJson两种数据格式的差异,必然会有存在不同的地方。

-

比如在XML的元素名中是不存在特殊字符的,可是JsonKey可以是任意的字符串,假如有一下Json数据:

-
-
{ "aa": { "bb": "cc" } }
-
-

JsonPath中我们可以通过路径aa.bb获取到值cc,这没有任何问题,如果Json数据换成下面的呢:

-
-
{ "a.a": { "bb": "cc" } }
-
-

很显然通过a.a.bb是无法得到值cc的。或许通常情况下Json都表示某个对象,而对象是不会存在这么奇怪的属性的。但是对于Json的数据格式而言,它的Key可以是任意字符。即便在浏览器中通过{OBJ_NAME}.a.a.bb也是只会得到报错。但是浏览器访问Json元素的方式并不是只用通过.的方式去获取,还有索引器{OBJ_NAME}['a.a']['bb']是可以得到正确值的。

-

STJsonPath中允许使用'或者",在词法分析器(STJsonPathTokenizer.cs)中会将其标记为String,若此字符串在非表达式的作用域内,解析器(STJsonPathParser.cs)会将其重新标记为Property作为索引使用,从而避免特殊字符串无法处理的情况,字符串支持\进行转义。所以在STJsonPath中可以使用'a.a'.bb获取到正确值。而在草案中也确实提到过类似的处理方式。

-

表达式疑问

-

在草案中有提到支持表达式,且表达式有两种类型,()?()。分别代表普通表达式过滤表达式。而按照作者的理解,普通表达式用于计算出一个值,且将这个值作为JsonPath的一部分,比如在很多案例中可以看到的$.books[(@.length - 1)]。在执行JsonPath的时候会可能对Json元素进行层层递归,其中@是一个动态变量,表示递归过程中当前正在处理的Json元素,根据表达式的表面意思是想获取books中的倒数第一个元素。

-

但是@.length如何被执行?length是哪里来的?如果@是一个数组,作者暂且可以理解为是在对数组求长度?可是如果@是一个对象呢?

-

既然是表达式,那么可以在()中写什么样的语法呢?语法规呢?关于这些疑问作者都采用了自己的实现方式,将在稍后的教程中介绍。

-

STJson API

-

数据类型关系

-
- - - - - - - - - - - - - - - - -
.NetSTJsonValueType.NetSTJsonValueType
byteLongsbyteLong
shortLongushortLong
intLonguintLong
longLongulongLong
floatDoubledoubleDouble
decimalDoubleboolBoolean
charStringstringString
DateTimeStringenumLong or String
PointArrayPointFArray
SizeArraySizeFArray
RectangleArrayRectangleFArray
ColorArrayDataTableObject
ArrayArrayICollectionArray
IDectionaryObjectobjectObject
-
-

STJson涵盖了常见的基本数据类型,即便不包含在其中,那么最后也会通过执行反射递归对象的属性。

-

静态函数

-
-

下列中(+n)表示有多个重载

-
-
- - - - - - - - - - - -
returnsignaturenote
stringSerialize(+n)object对象序列化为字符串。
STJsonDeserialize(string)将字符串转换为STJson对象序。
TDeserialize<T>(+n)将对象或字符串转换为目标对象。
STJsonSTJson CreateObject()创建一个空白对象。
STJsonSTJson CreateArray(params object[])创建一个数组对象。
STJsonFromObject(+n)将一个对象转换为STJson
stringFormat(+n)格式化一个Json字符串。
voidAddCustomConverter(+n)自定义类型转换器。
voidRemoveCustomConverter(+n)移除自定义转换器。
-
-

非静态函数

-
- - - - - - - - - - - - -
returnsignaturenote
STJsonSetItem(+n)向对象中添加一个键值对,并返回自己。
STJsonSetKey(string)向对象中添加一个key,并返回目标对象。
voidSetValue(+n)设置目标对象的值。
STJsonDelete(string)从对象中移除一个key,并返回目标对象。
STJsonAppend(+n)向数组对象中添加一个或一些元素,并返回自己。
STJsonInsert(+n)向数组对象中插入一个元素,并返回自己。
STJsonRemoveAt(int nIndex)从数组对象中删除一个索引。
voidClear()清空所有子元素。
IEnumerator<STJson>GetEnumerator()获取当前元素中所有子元素。
STJsonClone克隆当前元素。
-
-

扩展函数

-
- - - - - - - - - - - - - - - -
returnsignaturenote
STJsonSet(+n)根据路径(STJsonPath)设置对象。
stringGetValue(+n)获取对象的字符串值(仅值类型)
TGetValue<T>(+n)获取对象值(仅值类型)
STJsonSelect(+n)在对象中进行数据筛选。
STJsonSelectFirst(+n)在对象中进行数据筛选,并选中第一个结果。
STJsonSelectLast(+n)在对象中进行数据筛选,并选中最后一个结果。
STJsonGroup(+n)对指定字段进行分组。
STJsonTerms(+n)对指定字段的值进行个数统计。
STJsonSort(+n)对指定字段进行排序处理。
STJsonMin(+n)统计指定字段的最小值。
STJsonMax(+n)统计指定字段的最大值。
STJsonSum(+n)统计指定字段的总数。
STJsonAvg(+n)统计指定字段的平均值。
-
-

字段

-
- - - - - - - -
typenamenote
stringKey当前STJson父元素的Key
objectValue当前STJson的值,若当前STJson不是值类型则为null
boolIsNullObject当前STJson是否为空元素,即无法确定当前STJson值的数据类型。
intCount当前STJson所包含子元素的个数。
STJsonValueTypeValueType(枚举)当前元素的数据类型。
-
-

STJsonValueType为以下值:

-

Undefined String Boolean Long Double Datetime Array Object

-

索引器

-
    -
  • STJson STJson[int]
  • -
  • STJson STJson[string]
  • -
-

STJson [基本应用]

-

STJson是一个中间数据类型,它是stringobject之间的桥梁,使用非常便捷,比如:

-
-
var st_json = new STJson()
-    .SetItem("number", 0)               // 函数返回自身 所以可以连续操作
-    .SetItem("boolean", true)
-    .SetItem("string", "this is string")
-    .SetItem("datetime", DateTime.Now)
-    .SetItem("array_1", STJson.CreateArray(123, true, "string"))
-    .SetItem("array_2", STJson.FromObject(new object[] { 123, true, "string" }))
-    .SetItem("object", new { key = "this is a object" })
-    .SetItem("null", obj: null);
-st_json.SetKey("key").SetValue("this is a test");
-Console.WriteLine(st_json.ToString(4)); // 4 -> indentation space count
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "number": 0,
-    "boolean": true,
-    "string": "this is string",
-    "datetime": "2023-04-22T21:12:30.6109410+08:00",
-    "array_1": [
-        123, true, "string"
-    ],
-    "array_2": [
-        123, true, "string"
-    ],
-    "object": {
-        "key": "this is a object"
-    },
-    "null": null,
-    "key": "this is a test"
-}
-
-
-

当执行var st_json = new STJson()时,st_json为空元素,即st_json.IsNullObject = true。因为此时无法确定st_json对象还是数组或者是

-

STJson中不存在类似于JArrayJObject对象,STJson既可以是Array也可以是ObjectSTJson拥有两个索引器[int][string]

-
-
-
var json_1 = new STJson();
-Console.WriteLine("[json_1] - " + json_1.IsNullObject + " - " + json_1.ValueType);
-
-var json_2 = new STJson();
-json_2.SetItem("key", "value");
-Console.WriteLine("[json_2] - " + json_2.IsNullObject + " - " + json_2.ValueType);
-
-var json_3 = new STJson();
-json_3.Append(1, 2, 3);
-Console.WriteLine("[json_3] - " + json_3.IsNullObject + " - " + json_3.ValueType);
-
-var json_4 = new STJson();
-json_4.SetValue(DateTime.Now);
-Console.WriteLine("[json_4] - " + json_4.IsNullObject + " - " + json_4.ValueType);
-
-var json_5 = STJson.CreateArray();          // made by static function
-Console.WriteLine("[json_5] - " + json_5.IsNullObject + " - " + json_5.ValueType);
-
-var json_6 = STJson.CreateObject();         // made by static function
-Console.WriteLine("[json_6] - " + json_6.IsNullObject + " - " + json_6.ValueType);
-
-var json_7 = STJson.FromObject(12);         // made by static function
-Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType);
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[json_1] - True - None
-[json_2] - False - Object
-[json_3] - False - Array
-[json_4] - False - Datetime
-[json_5] - False - Array
-[json_6] - False - Object
-[json_7] - False - Long
-
-
-

ValuteType = Array时,无法调用SetItem(+n),当ValueType = Object时,无法调用Append(+n)。但是通过SetValue(+n)可强制改变ValueType

-
-

正如上面提到的STJson有两个索引器,可以通过索引器访问他们,或者获取值。

-
-
var json_temp = STJson.CreateArray()
-    .SetItem("string", "this is string")
-    .SetItem("array", new Object[] { "1", "2", "3" });
-Console.WriteLine(json_temp["string"]);
-Console.WriteLine(json_temp["string"].GetValue());
-Console.WriteLine(json_temp["array"][1]);
-Console.WriteLine(json_temp["array"][1].GetValue<long>());
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-"this is string"
-this is string
-"2"
-2
-
-

object -> string

-

通过上面的例子或许你已经知道怎么将一个对象转换为string,通过STJson.FromObject(object).ToString(+n)即可,但是有没有可能,其实不用这么麻烦的?比如:STJson.Serialize(+n)就可以了???

-

事实上STJson.Serialize(+n)的效率会更好,因为它是直接将对象转换为字符串,而不是转换成STJson再转换成字符串。

-
-
Console.WriteLine(STJson.Serialize(new { key = "this is test" }));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{"key":"this is test"}
-
-

当然你可以有个更友好的输出格式:

-
-
Console.WriteLine(STJson.Serialize(new { key = "this is test" }, 4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "key": "this is test"
-}
-
-

事实上格式化输出是通过调用静态函数STJson.Format(+n)完成的。如果你觉得不喜欢作者内置的格式化风格,完全可以自己写一个格式化的函数。或者去修改源码文件STJson.Statics.cs:Format(string,int)

-

string -> object

-

事实上代码并不会直接将string转换为object。因为在那之前必须先对字符串进行解析,确保它是一个正确格式的Json。但是做完这个过程的时候已经得到一个STJson对象了。最后将STJson再转换为object

-

所以你会在源代码STLib.Json.Converter中看到如下文件:

-

ObjectToSTJson.cs ObjectToString.cs STJsonToObject.cs StringToSTJson.cs

-

里面并没有StringToObject.cs文件,而STJson.Deserialize(+n)的源码如下:

-
-
public static T Deserialize<T>(string strJson, +n) {
-    var json = StringToSTJson.Get(strJson, +n);
-    return STJsonToObject.Get<T>(json, +n);
-}
-
-

STJson -> object

-

如何将字符串转换为对象,相信作者不用说明读者也应该知道如何处理,但是这里值得说明的是,STJson可以附加到对象中,实现局部更新。

-
-
public class TestClass {
-    public int X;
-    public int Y;
-}
-
-TestClass tc = new TestClass() {
-    X = 10,
-    Y = 20
-};
-STJson json_test = new STJson().SetItem("Y", 100);
-STJson.Deserialize(json_test, tc);
-Console.WriteLine(STJson.Serialize(tc));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
- {"X":10,"Y":100}
-
-

STJsonConverter

-

虽然在STJson中内置了很多数据类型的转换,即便没有的数据类型也会被当做object做递归处理。但是有时情况并不是很友好。比如:

-
-
Rectangle rect = new Rectangle(10, 10, 100, 100);
-Console.WriteLine(STJson.Serialize(rect, 4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "Location": {
-        "IsEmpty": false,
-        "X": 10,
-        "Y": 10
-    },
-    "Size": {
-        "IsEmpty": false,
-        "Width": 100,
-        "Height": 100
-    },
-    "X": 10,
-    "Y": 10,
-    "Width": 100,
-    "Height": 100,
-    "Left": 10,
-    "Top": 10,
-    "Right": 110,
-    "Bottom": 110,
-    "IsEmpty": false
-}
-
-

很显然,这个结果过于复杂,因为Rectangle的所有字段都被递归出来了。但是,如果这样呢?

-
-
public class RectangleConverter : STJsonConverter
-{
-    public override object JsonToObject(Type t, STJson json, ref bool bProcessed) {
-        return new Rectangle(
-            json["x"].GetValue<int>(),
-            json["y"].GetValue<int>(),
-            json["w"].GetValue<int>(),
-            json["h"].GetValue<int>());
-    }
-
-    public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) {
-        Rectangle rect = (Rectangle)obj;
-        return STJson.New()
-            .SetItem("x", rect.X)
-            .SetItem("y", rect.Y)
-            .SetItem("w", rect.Width)
-            .SetItem("h", rect.Height);
-    }
-
-    public override string ObjectToString(Type t, object obj, ref bool bProcessed) {
-        //return "{\"x\":" + ... + "}"
-        var json = this.ObjectToJson(t, obj, ref bProcessed);
-        if (bProcessed) {
-            return json.ToString();
-        }
-        return null;
-    }
-}
-
-Rectangle rect = new Rectangle(10, 10, 100, 100);
-STJson.AddCustomConverter(typeof(Rectangle), new RectangleConverter());
-string strResult = STJson.Serialize(rect);
-Console.WriteLine(strResult);
-rect = STJson.Deserialize<Rectangle>(strResult.Replace("100", "200"));
-Console.WriteLine(rect);
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{"x": 10,"y": 10,"w": 100,"h": 100}
-{X=10,Y=10,Width=200,Height=200}
-
-
-

其中bProcessed默认传入值为true,当上一层函数获取到false时则使用默认的处理方式。

-
-

STJsonConverter提供了Attribute类,也可用于标记对象属性。

-
-
public class Test{
-    [STJsonConverter(typeof(RectangleConverter))]
-    public Rectangle Rect{get; set;}
-}
-
-STJsonConverter.cs -
-
public abstract class STJsonConverter
-{
-    public virtual STJson ObjectToJson(Type t, object obj, ref bool bProcessed) {
-        bProcessed = false;
-        return null;
-    }
-    public virtual string ObjectToString(Type t, object obj, ref bool bProcessed) {
-        bProcessed = false;
-        return null;
-    }
-    public virtual object JsonToObject(Type t, STJson json, ref bool bProcessed) {
-        bProcessed = false;
-        return null;
-    }
-}
-
-

STJsonAttribute

-

或许在序列化的时候你并不想输出所有的属性,那么可以通过STJsonAttribute去控制。

-
-
[STJson(STJsonSerilizaMode.Include)]    // optional
-public class Student
-{
-    [STJsonProperty("test_name")]
-    public string Name;
-    public int Age;
-    public Gender Gender;
-    [STJsonProperty]                        // optional
-    public List<string> Hobby;
-}
-
-public enum Gender
-{
-    Male, Female
-}
-
-var stu = new Student() {
-    Name = "Tom",
-    Age = 100,
-    Gender = Gender.Male,
-    Hobby = new List<string>() { "Game", "Sing" }
-};
-
-str = STJson.Serialize(stu);
-Console.WriteLine(str);
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{"test_name":"Tom","Hobby":["Cooking","Sports"]}
-
-

STJsonSetting

-

STJsonSetting用于在序列化或者反序列化中添加一些个性化设置。原本设置是全局的。但随着一些设置项的增多,作者认为全局设置粘性太高,所以独立出来STJsonSetting用于解耦。同时独立的设置类也可以方便后续版本的功能扩展,当然后续版本的概率不是很大。除非作者不想当咸鱼了。

-
-
var stu = new Student() {
-    Name = "Tom",
-    Age = 100,
-    Gender = Gender.Male,
-    Hobby = new List<string>() { "Game", "Sing" }
-};
-STJsonSetting setting = new STJsonSetting();
-setting.EnumUseNumber = true;
-setting.IgnoreAttribute = true;
-setting.Mode = STJsonSettingKeyMode.Exclude;
-setting.KeyList.Add("Age");
-str = STJson.Serialize(stu, setting);
-Console.WriteLine(STJson.Format(str));
-STJson.Deserialize<Student>(str);
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "test_name": "Tom",
-    "Gender": 0,
-    "Hobby": [
-        "Game", "Sing"
-    ]
-}
-
-
-

Attribute的优先级大于STJsonSetting

-
-

json_src

-

在接下来的教程中我们会使用到一些测试数据,数据如下:

-test.json -
-
[{
-    "name": "Tom", "age": 16, "gender": 0,
-    "hobby": [
-        "cooking", "sing"
-    ]
-},{
-    "name": "Tony", "age": 16, "gender": 0,
-    "hobby": [
-        "game", "dance"
-    ]
-},{
-    "name": "Andy", "age": 20, "gender": 1,
-    "hobby": [
-        "draw", "sing"
-    ]
-},{
-    "name": "Kun", "age": 26, "gender": 1,
-    "hobby": [
-        "sing", "dance", "rap", "basketball"
-    ]
-}]
-
-

将其加载到程序中:

-
-
var json_src = STJson.Deserialize(System.IO.File.ReadAllText("./test.json"));
-
-
-

之后的案例中出现json_src则为以上对象。

-
-

STJsonPath

-

在源码STJsonExtension.cs中对STJson的功能进行了扩展,里面集成一些STJsonPath的功能。所以在STJson的原始代码中并没有对STJsonPath的依赖,STJson可独立使用。但STJsonPath作为STJson的辅助类,需依赖STJson

-

选择器

-
- - - - - - - - - - - - -
tokennote
$根节点选择器,可视作代表根节点对象。
@当前元素选择器,在遍历过程中指代当前被遍历的元素。
*通配符,表示可以代表任何一个节点。
.<name>子节点选择器,指定子节点的key
..深度选择器,表示可以是任意路径。
['<name>'(,'<name>')]列表选择器,指定子节点的key集合。
[<number>(,(number))]列表选择器,指定子节点的index集合。
[start:end:step]切片选择器,用于指定索引区间。
[(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果作为索引继续向下选择。
[?(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果转换为布尔值,决定是否继续选择。
-
-

使用方式

-

通过以下方式可以构建一个STJsonPath

-
-
// var jp = new STJsonPath("$[0]name");
-// var jp = new STJsonPath("$[0].name");
-var jp = new STJsonPath("[0]'name'"); // 以上方式均可以使用 $不是必须的
-Console.WriteLine(jp.Select(json_src));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-["Tom"]
-
-

当然在STJson中的扩展函数中已经集成STJsonPath,可以通过下面的方式直接使用:

-
-
// var jp = new STJsonPath("[0].name");
-// Console.WriteLine(json_src.Select(jp));
-Console.WriteLine(json_src.Select("[0].name")); // 内部动态构建 STJsonPath
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-["Tom"]
-
-
-

STJsonPath以数组的方式返回数据,其返回值是STJson而不是List<STJson>STJson也可以是数组对象。

-

$开头对于STJsonPath来说并不是必须的,且内部会移除掉开头的$或者@$@仅在表达式中作为对象的变量使用。

-
-
-

在表达式中选择器的返回值仅返回选中的第一个结果。而不是数组列表,这点将在后面说明。

-
-

STJsonPath中允许使用'或者",比如:'a.b' "a.b" STJsonPath会将其视为一个独立的个体。而不是两个。列如有如下Json

-
-
{
-    "a.b": "this is a test"
-}
-
-

很明显通过Select("a.b")是无法获取到数据的,需要通过Select("'a.b'")

-
-
string strTemp = "{\"a.b\": \"this is a test\"}";
-var json_temp = STJson.Deserialize(strTemp);
-Console.WriteLine(json_temp.Select("a.b"));
-Console.WriteLine(json_temp.Select("'a.b'"));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[]
-["this is a test"]
-
-

在字符串中支持\进行转义:\r\n\t\f\b\a\v\0\x..\u...\.

-

通配符

-

通配符可表示当前层级中的任何一个节点。获取所有人员姓名。

-
-
Console.WriteLine(json_src.Select("*.name").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "Tom", "Tony", "Andy", "Kun"
-]
-
-

深度选择器

-

深度选择器与通配符类似,但深度选择器可以是任意层级。

-
-
Console.WriteLine(json_src.Select("..name").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "Tom", "Tony", "Andy", "Kun"
-]
-
-

列表选择器

-

列表选择器支持intstring两种方式,虽然在上面选择器的表格中列举了两个列表选择器,但是在STJsonPath中只有一个列表选择器,它们可以混合使用,比如下面的使用方式都是合法的:

-
    -
  • [0, 1, 2, 3]
  • -
  • ['key_1', 'key_2', key_3]
  • -
  • [0, 'key_1', 1, 'key_2', 2, key_3]
  • -
-

STJsonPath在内部会自动拆分为两个列表选择器,并判断STJsonValueType决定使用哪个列表选择器。内部实现代码如下:

-
-
case STJsonPathItem.ItemType.List:
-    if (jsonCurrent.ValueType == STJsonValueType.Object) {
-        foreach (var v in item.Keys) {
-            if (jsonCurrent[v] == null) {
-                continue;
-            }
-            // ...
-        }
-    }
-    if (jsonCurrent.ValueType == STJsonValueType.Array) {
-        foreach (var v in item.Indices) {
-            nIndexSliceL = v;
-            if (nIndexSliceL < 0) nIndexSliceL = jsonCurrent.Count + nIndexSliceL;
-            if (nIndexSliceL < 0) continue;
-            if (nIndexSliceL >= jsonCurrent.Count) continue;
-            // ...
-        }
-    }
-    break;
-
-

选择索引为02的元素。

-
-
Console.WriteLine(json_src.Select("[0,2]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tom",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "cooking", "sing"
-        ]
-    }, {
-        "name": "Andy",
-        "age": 20,
-        "gender": 1,
-        "hobby": [
-            "draw", "sing"
-        ]
-    }
-]
-
-

对于int索引可以使用负数,比如-1则表示获取最后一个元素。当STJsonPath检测到负数时候会执行STJson.Count - n将结果作为索引。

-
-
//Console.WriteLine(json_src.Select("-1").ToString(4));
-Console.WriteLine(json_src.Select("[-1]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Kun",
-        "age": 26,
-        "gender": 1,
-        "hobby": [
-            "sing", "dance", "rap", "basketball"
-        ]
-    }
-]
-
-

切片选择器

-

切片选择器用于在数组中选择一个片段,切片选择器默认值[0:-1:1],在切片选择器内部如下实现:

-
-
case STJsonPathItem.ItemType.Slice:
-    if (jsonCurrent.ValueType != STJsonValueType.Array) {
-        return;
-    }
-    if (nIndexSliceL < 0) nIndexSliceL = jsonCurrent.Count + nIndexSliceL;
-    if (nIndexSliceR < 0) nIndexSliceR = jsonCurrent.Count + nIndexSliceR;
-    if (nIndexSliceL < 0) nIndexSliceL = 0;
-    else if (nIndexSliceL >= jsonCurrent.Count) nIndexSliceL = jsonCurrent.Count - 1;
-    if (nIndexSliceR < 0) nIndexSliceR = 0;
-    else if (nIndexSliceR >= jsonCurrent.Count) nIndexSliceR = jsonCurrent.Count - 1;
-    if (nIndexSliceL > nIndexSliceR) {
-        for (int i = nIndexSliceL; i >= nIndexSliceR; i -= item.Step) {
-            // ...
-        }
-    } else {
-        for (int i = nIndexSliceL; i <= nIndexSliceR; i += item.Step) {
-            // ...
-        }
-    }
-    break;
-
-

所以切片中的三个值等同于for循环中的三个条件,所以原理与效果就不再说明。

-
- - - - - - -
expressionrangenote
[::]0 <= R <= (OBJ).length - 1等同于*
[5:]5 <= R <= {OBJ}.length - 1从第6个元素开始,获取所有元素
[-1:0]{OBJ}.length - 1 >= R >= 0倒序获取数据
[0::2]0 <= R <= {OBJ}.length - 1顺序获取数据,且间隔一个数据
-
-
-

切片选择器中至少出现一个:step大于0,否则将获得异常。

-
-
-
Console.WriteLine(json_src.Select("[-1:]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Kun",
-        "age": 26,
-        "gender": 1,
-        "hobby": [
-            "sing", "dance", "rap", "basketball"
-        ]
-    }
-]
-
-

表达式

-

[?()]中可支持下列运算符,优先级从上至下依次升高

-
-
    -
  • && ||
  • -
  • < <= > >= == != re
  • -
  • & | << >> ^ ~
  • -
  • + -
  • -
  • * / %
  • -
  • in nin anyof
  • -
  • !
  • -
-
- - - - - - -
operatornotee.g
re正则表达式[?(@.name re 'un')]
in左边的值或数组包含在右边的数组中[?(@.age in [16,20])]
nin左边的值或数组不包含在右边的数组中[?(@.hobby nin ['sing','draw'])]
anyof左边的值或数组和右边的数组存在交集[?(@.hobby anyof ['sing','draw'])]
-
-

表达式有两种模式:

-
    -
  • [?(<expression>)] - 过滤表达式,用于计算出一个布尔值,确定是否继续匹配。
  • -
  • [(<expression>)] - 普通表达式,用于计算出一个值,并将值作为索引继续匹配。
  • -
-

过滤表达式

-

选中name中包含字母ku的元素:

-
-
//Console.WriteLine(json_src.Select("*.[?(@.name == 'kun')]").ToString(4));
-Console.WriteLine(json_src.Select("*.[?(@.name re '(?i)ku')]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Kun",
-        "age": 26,
-        "gender": 1,
-        "hobby": [
-            "sing", "dance", "rap", "basketball"
-        ]
-    }
-]
-
-

(?i)中的i表示忽略大小写,其正则表达式以.NetRegex为标准。(?...)开头则表示设置匹配模式。至于匹配模式自行查阅相关资料。

-

选中hobby不包含singswing的元素:

-
-
Console.WriteLine(json_src.Select("*.[?(@.hobby nin ['sing','draw'])]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tony",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "game", "dance"
-        ]
-    }
-]
-
-

普通表达式

-

普通表达式会将结果作为STJsonPath的部分继续匹配。

-
-
Console.WriteLine(json_src.Select("*.[('na' + 'me')]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "Tom", "Tony", "Andy", "Kun"
-]
-
-

[('na' + 'me')]'na' + 'me'的结果为'name',并且会将这个值作为索引,所以上述效果等同于*.name,当然返回值也可以是一个集合。

-
-
Console.WriteLine(json_src.Select("*.[(['na' + 'me', 'age', 0, 1 + 1])]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "Tom", 16, "Tony", 16, "Andy", 20, "Kun", 26
-]
-
-

上面表达式的结算结果值为['name', 'age', 0, 2]。但是很显然02将不会起到任何作用,因为第二层的数据对象并不是一个数组。

-

上面的表达式等同于*.['name', 'age', 0, 2]。如果将上面的换成第三层会得到下面的结果。

-
-
Console.WriteLine(json_src.Select("*.*.[(['na' + 'me', 'age', 0, 1 + 1])]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "cooking", "game", "draw", "sing", "rap"
-]
-
-

可以看到'name''age'对于hobby来说是无效的,因为hobby是一个数组。

-

测试表达式

-

可能读者并不了解表达式在内部是如何被执行了并且会输出什么样的结果,作者提供了一个静态测试函数TestExpression()可用于调试表达式。若有什么不明白的地方测试一下就会看到过程及结果。

-
-
Console.WriteLine(STJsonPath.TestExpression(
-    null,           // [STJson] 用于替代表达式中出现的 $
-    null,           // [STJson] 用于替代表达式中出现的 @
-    "1+2+3"         // 表达式文本
-    ).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "type": "expression",
-    "parsed": "{1 + 2 + 3}",        // 格式化后的文本 {}表示此部分需要单独执行 如: [1, {1+1}, 3]
-    "polish": [
-        "1", "2", " + ", "3", " + " // 逆波兰方式排列
-    ],
-    "steps": [                      // 执行步骤
-        {
-            "type": "excute",
-            "operator": "+",
-            "get_left_token": {     // 计算操作符左边元素的值,表达式左边也可能是一个表达式
-                "parsed": "1",
-                "type": "value",
-                "result": {
-                    "value_type": "Long",
-                    "text": "1"
-                }
-            },
-            "get_right_token": {
-                "parsed": "2",
-                "type": "value",
-                "result": {
-                    "value_type": "Long",
-                    "text": "2"
-                }
-            },
-            "result": {             // 该步骤执行结果
-                "value_type": "Long",
-                "text": "3"
-            }
-        }, {
-            "type": "excute",
-            "operator": "+",
-            "get_left_token": {     // 此时操作符左边的元素为上一步的计算结果
-                "parsed": "3",
-                "type": "value",
-                "result": {
-                    "value_type": "Long",
-                    "text": "3"
-                }
-            },
-            "get_right_token": {
-                "parsed": "3",
-                "type": "value",
-                "result": {
-                    "value_type": "Long",
-                    "text": "3"
-                }
-            },
-            "result": {
-                "value_type": "Long",
-                "text": "6"
-            }
-        }
-    ],
-    "check_result": {               // 清空波兰表达式数据栈,确定最终输出结果。
-        "parsed": "6",
-        "type": "value",
-        "result": {
-            "value_type": "Long",
-            "text": "6"
-        }
-    },
-    "return": {                     // 最终返回值
-        "value_type": "Long",
-        "text": "6",
-        "bool": true                // 如果用作布尔表达式则转换为 true
-    }
-}
-
-

如果过程不重要,仅仅是想看执行结果。

-
-
Console.WriteLine(STJsonPath.TestExpression(
-    null,           // [STJson] 用于替代表达式中出现的 $
-    null,           // [STJson] 用于替代表达式中出现的 @
-    "1+2+3"         // 表达式文本
-    ).SelectFirst("return").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "return": {                     // 最终返回值
-        "value_type": "Long",
-        "text": "6",
-        "bool": true                // 如果用作布尔表达式则转换为 true
-    }
-}
-
-

内置函数

-
- - - - - - - - - - - - -
returnsignaturenote
stringtypeof(+n)获取数据类型。
stringstr(+n)转换为字符串。
stringupper(+n)转换为大写。
stringlower(+n)转换为小写。
longlen(+n)获取字符串或者数组长度。
longlong(+n)转换为整数。
doubledouble(+n)转换为浮点数。
long or doubleabs(+n)获取绝对值。
longround(+n)四舍五入。
longceil(+n)向上取整。
-
-

typeof具有下列返回值:

-

string long double boolean array object undefined

-

以上函数可使用如下签名

-
    -
  • fn(object)
  • -
  • fn(object[])
  • -
  • fn(object,bool)
  • -
  • fn(object[],bool)
  • -
-

其中bool表示是否期望以数组方式获得结果,如果为true则以数组方式输出结果,并且当object为数组是,递归其中的数据。

-
-
Console.WriteLine(STJsonPath.TestExpression(
-    null,
-    null,
-    "typeof([1+2+3,'abc'], false)"
-    ).SelectFirst("return").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "bool": true,
-    "value_type": "String",
-    "value": "array"
-}
-// =============================================================================
-Console.WriteLine(STJsonPath.TestExpression(
-    null,
-    null,
-    "typeof([1+2+3,'abc'], true)"
-    ).SelectFirst("return").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "bool": true,
-    "value_type": "Array",
-    "items": [
-        "long", "string"
-    ]
-}
-
-

不同的函数第二个参数采用不同的默认值。一下是一些其他函数,读者可以自己通过TestExpression()进行效果测试。

-
- - - - - - - - - - - -
returnsignaturenote
long or doublemax(+1)求最大值。
long or doublemin(+1)求最小值。
long or doubleavg(+1)求平均值。
long or doublesum(+1)求总和。
stringtrim(+n)裁切字符串两端的指定字符。
stringtrims(+n)裁切字符串开始的指定字符。
stringtrime(+n)裁切字符串末尾的指定字符。
string_arraysplit(+1)拆分字符串。
long or stringtime(+n)获取或格式化时间戳。
-
-

内置函数列表

-

随着版本的迭代更新(如果可能的话),内置函数可能随时发生变化,通过GetBuildInFunctionList()可以查看当前版本支持的内置函数信息。

-
-
Console.WriteLine(STJsonPath.GetBuildInFunctionList().ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "typeof",
-        "demos": [
-            "(object) -> typeof('abc')", "(array,bool) -> typeof(['abc',123],true)"
-        ]
-    },
-    ...
-]
-
-

读者可以通过TestExpression()按照demos对函数进行测试。

-

选中hobby长度大于2的元素:

-
-
Console.WriteLine(json_src.Select("..[?(len(@.hobby) > 1 + 1)]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Kun",
-        "age": 26,
-        "gender": 1,
-        "hobby": [
-            "sing", "dance", "rap", "basketball"
-        ]
-    }
-]
-
-

自定义函数

-

在为表达式提供内置函数的时候作者也不知道开发者期望内置什么样的函数,随便内置了几个之后干脆不想了。没有什么问题是写代码解决不了的,没错。。。让开发者自己去写。但是开发者要怎么样为STJsonPath提供函数呢?

-

STJsonPath.CustomFunctions是一个静态字典。用于保存开发者自定义的函数。其函数签名如下:

-
-
public delegate STJson STJsonPathCustomFuncHander(STJsonPathExpFuncArg[] args);
-
-public struct STJsonPathExpFuncArg
-{
-    public STJsonPathExpFuncArgType Type;
-    public object Value;
-}
-
-public enum STJsonPathExpFuncArgType
-{
-    Undefined, Number, String, Boolean, Array, Object,
-}
-
-

自定义函数返回值统一STJson,因为STJson可与STJsonPath完美衔接,在STJsonPath可直接使用选择器对自定义函数返回值进行操作。比如下面的案例为STJsonPath添加一个matches函数用于正则表达式操作。

-
-
STJsonPath.CustomFunctions.Add("matches", (objs) => {
-    var json_ret = new STJson();
-    json_ret.SetItem("count", 0);
-    json_ret.SetItem("values", STJson.CreateArray());
-    if (objs.Length != 2) {
-        return json_ret;
-    }
-    var ms = Regex.Matches(objs[0].Value.ToString(), objs[1].Value.ToString());
-    json_ret["count"].SetValue(ms.Count);
-    foreach (Match v in ms) {
-        json_ret["values"].Append(STJson.FromObject(new {
-            success = v.Success,
-            index = v.Index,
-            length = v.Length,
-            value = v.Value
-        }));
-    }
-    return json_ret;
-});
-
-

然后我们需要筛选出hobby中包含两个ao的元素:

-
-
Console.WriteLine(json_src.Select("*.hobby.*[?(matches(@,'a|o').count == 2)]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "cooking", "basketball"
-]
-
-
-

自定义函数优先级高于内置函数,也就是说如果在自定义函数中和内置函数中出现了同名函数,则优先调用自定义函数

-
-

表达式中的选择器

-

在表达式中的选择器仅返回选中的第一个结果,而不是数组列表。

-
-
Console.WriteLine(json_src[0].Select("name").ToString(4));
-Console.WriteLine(STJsonPath.TestExpression(json_src[0], json_src[0], "@.name").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "Tom"
-]
-{
-    "type": "expression",
-    "parsed": "{[@]['name']}",
-    "polish": [
-        "[@]['name']"
-    ],
-    "steps": [
-
-    ],
-    "check_result": {
-        "parsed": "[@]['name']",
-        "type": "selector",
-        "root_json": "{\"name\":\"Tom\",\"age\":16,\"gender\":0,\"hobby\":[\"cooking\",\"sing\"]}",
-        "current_json": "{\"name\":\"Tom\",\"age\":16,\"gender\":0,\"hobby\":[\"cooking\",\"sing\"]}",
-        "selected_json": "[\"Tom\"]",
-        "result": {                     // 返回值将只获取第一个结果
-            "value_type": "String",
-            "text": "Tom"
-        }
-    },
-    "return": {
-        "bool": true,
-        "value_type": "String",
-        "text": "Tom"
-    }
-}
-
-

选择方式

-

普通模式(默认方式):

-
-
Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.ItemOnly).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "Tom", "Tony", "Andy", "Kun"
-]
-
-

路径模式:

-
-
Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.ItemWithPath).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "path": [
-            0, "name"
-        ],
-        "item": "Tom"
-    }, {
-        "path": [
-            1, "name"
-        ],
-        "item": "Tony"
-    }, {
-        "path": [
-            2, "name"
-        ],
-        "item": "Andy"
-    }, {
-        "path": [
-            3, "name"
-        ],
-        "item": "Kun"
-    }
-]
-
-

保持结构:

-
-
Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.KeepStructure).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tom"
-    }, {
-        "name": "Tony"
-    }, {
-        "name": "Andy"
-    }, {
-        "name": "Kun"
-    }
-]
-
-

ParsedTokens

-

GetParsedTokens()用于获取当前STJsonPath得字符串在内部是如何被解析,且以STJson方式输出。如果你也想编写一个解析器,说不定可以给你提供一些思路。

-
-
Console.WriteLine(
-    new STJsonPath("$..[?(matches(@.name,'u').count == 1)]").GetParsedTokens().ToString(2)
-    );
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-  "type": "entry",
-  "parsed": "[..]{matches({[@]['name']}, {'u'}) == 1}",
-  "items": [
-    {
-      "type": "selector_item",
-      "item_type": "Depth",
-      "value": ".."
-    }, {
-      "type": "expression",
-      "parsed": "{matches({[@]['name']}, {'u'}) == 1}",
-      "items": [
-        {
-          "type": "function",
-          "parsed": "matches({[@]['name']}, {'u'})",
-          "name": "matches",
-          "args": {
-            "parsed": "({[@]['name']}, {'u'})",
-            "items": [
-              {
-                "type": "expression",
-                "parsed": "{[@]['name']}",
-                "items": [
-                  {
-                    "type": "expression_item",
-                    "item_type": "selector",
-                    "items": [
-                      {
-                        "type": "selector_item",
-                        "item_type": "Current",
-                        "value": "@"
-                      }, {
-                        "type": "selector_item",
-                        "item_type": "List",
-                        "value": [
-                          "name"
-                        ]
-                      }
-                    ]
-                  }
-                ]
-              }, {
-                "type": "expression",
-                "parsed": "{'u'}",
-                "items": [
-                  {
-                    "type": "expression_item",
-                    "item_type": "string",
-                    "value": "u"
-                  }
-                ]
-              }
-            ]
-          },
-          "selector": {
-            "parsed": "['count']",
-            "items": [
-              {
-                "type": "selector_item",
-                "item_type": "List",
-                "value": [
-                  "count"
-                ]
-              }
-            ]
-          }
-        }, {
-          "type": "expression_item",
-          "item_type": "long",
-          "value": 1
-        }, {
-          "type": "expression_item",
-          "item_type": "operator",
-          "value": "=="
-        }
-      ]
-    }
-  ]
-}
-
-

STJson [高级应用]

-

获取值

-

试想一个场景,作为一个WEB后端服务,需要处理前端提交过来的Json数据,假定我们期望得到以下Json数据:

-
-
{
-    "type": "get_list",
-    "page": {
-        "from": 100,
-        "size": 10
-    },
-    "other": {}
-}
-
-

并且我们已经将上面的数据转化为STJson且命名为json_post。其中fromsize用于翻页功能。那么后台可能会做如下判断:

-
-
if(json_post["page"] == null) { /* do something */ }
-if(json_post["page"]["from"] == null) { /* do something */ }
-if(json_post["page"]["from"].ValueType != STJsonValueType.Long) {
-    /* do something */
-}
-int nFrom = json_post["page"]["from"].GetValue<int>();
-// 或者 直接暴力一点
-int nFrom = 0;
-try{
-    nFrom = json_post["page"]["from"].GetValue<int>();
-}catch{
-    /* do something */
-}
-
-

很显然上面的代码让你抓狂。。。当然为了减少麻烦可以在后端代码中为其创建一个实体对象,然后将Json绑定到实体对象中。。。但是为每个Post过来的数据类型创建一个实体对象是不是也挺麻烦的。但是如果使用下面的代码。

-
-
int nFrom = json_post.GetValue<int>("page.from");
-
-

或许你会疑惑,,如果路径不存在或者from根本不是数字怎么办?emm....上面的代码依然会报错,因为上面的代码内部调用是:

-
-
public static T GetValue<T>(this STJson json, string strJsonPath) {
-    return json.GetValue<T>(new STJsonPath(strJsonPath));
-}
-
-public static T GetValue<T>(this STJson json, STJsonPath jsonPath) {
-    var j = jsonPath.SelectFirst(json);
-    if (j == null) {
-        throw new STJsonPathException("Can not selected a object with path {" + jsonPath.SourceText + "}");
-    }
-    var t = typeof(T);
-    bool bProcessed = true;
-    var convert = STJsonBuildInConverter.Get(t);
-    if (convert != null) {
-        var value = convert.JsonToObject(t, json, ref bProcessed);
-        if (bProcessed) {
-            return (T)value;
-        }
-    }
-    return (T)j.Value;
-}
-
-

因为当不存在元素时候,不知道需要返回什么值,可能读者认为无法获取值,返回一个默认值就好了,的确可以这么设计,可是调用者如何确定返回的值是真实的值还是默认值。无法确定是否异常了。除非强制指定一个默认值。

-
-
int nFrom = json_post.GetValue<int>("page.from", 0);
-
-

如果存在元素且能正常转换则返回元素值,否则返回0

-

或者想知道是否返回的是真实值:

-
-
int nFrom = 0;
-var bFlag = json_src.GetValue<int>("page.from", out nFrom);
-
-
-

若填写的strJsonPath会获取到多个值,则取第一个值。

-
-

设置值

-

如果我们需要手动构造一个Json可以通过STJson.SetItem()来添加元素。可是有些场景这样使用似乎有点麻烦,比如以ElasticSearch数据库的query语法为例。我们需要构造一个下面的数据:

-
-
{
-    "query":{
-        "term":{
-            "field_name": "field_value"
-        }
-    }
-}
-
-

那么按照之前的写法我们需要这样去构造一个STJson对象:

-
-
var json = new STJson()
-    .SetItem("query", new STJson()
-        .SetItem("term", new STJson()
-            .SetItem("field_name", "field_value"))
-        );
-// or
-var json = STJson.FromObject(new {
-    query = new {
-        term = new {
-            field_name = "field_value"
-        }
-    }
-});
-
-

但是还可以这样写代码:

-
-
var json = new STJson().Set("query.term.field_name", "field_value");
-Console.WriteLine(json.ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "query":{
-        "term":{
-            "field_name": "field_value"
-        }
-    }
-}
-
-

甚至是这样:

-
-
var json = new STJson().Set("array[0:4].[key_1,key_2]", "value");
-Console.WriteLine(json.ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "array": [
-        {
-            "key_1": "value",
-            "key_2": "value"
-        }, {
-            "key_1": "value",
-            "key_2": "value"
-        }, {
-            "key_1": "value",
-            "key_2": "value"
-        }, {
-            "key_1": "value",
-            "key_2": "value"
-        }, {
-            "key_1": "value",
-            "key_2": "value"
-        }
-    ]
-}
-
-
-

Set中仅仅支持列表选择器切片选择器。不支持其他选择器。

-
-

回调函数

-

通过上面的Set我们是否能够将json_src中每个人的喜好都添加一个coding?。。似乎不太可以。毕竟向hobby中添加数据需要append而不是set

-

当然也并不是没有办法。

-
-
json_src.Select("*.hobby", (p, j) => {
-    j.Append("coding");
-});
-Console.WriteLine(json_src.ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tom",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "cooking", "sing", "coding"
-        ]
-    },
-    ...
-]
-
-

Select中支持两种回调函数,一个需要返回值,另一个不需要返回值,如同上面的。而下面的带返回值。

-
-
var json = json_src.Select("*.hobby.*", (p, j) => {
-    return new STJsonPathCallBackResult() {
-        Selected = true,            // 是否将此条 json 添加到结果中
-        Json = new STJson()         // 需要被添加到结果的 json
-            .SetItem("path", p)
-            .SetItem("item", j.Value.ToString().ToUpper())
-    };
-});
-Console.WriteLine(json.ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "path": [
-            0, "hobby", 0
-        ],
-        "item": "COOKING"
-    },
-    ...
-]
-
-

PathItem

-

STJsonPath中有一个比较特殊的数据结构。

-
-
[
-    {
-        "path": [
-            ...
-        ],
-        "item": ...
-    },
-    ...
-]
-
-

可以通过RestorePathJson()还原其结构。

-
-
var json = json_src.Select("*.hobby.*", (p, j) => {
-    return new STJsonPathCallBackResult() {
-        Selected = true,
-        Json = new STJson()
-            .SetItem("path", p)
-            .SetItem("item", j.Value.ToString().ToUpper())
-    };
-});
-Console.WriteLine(STJsonPath.RestorePathJson(json).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "hobby": [
-            "COOKING", "SING"
-        ]
-    }, {
-        "hobby": [
-            "GAME", "DANCE"
-        ]
-    }, {
-        "hobby": [
-            "DRAW", "SING"
-        ]
-    }, {
-        "hobby": [
-            "SING", "DANCE", "RAP", "BASKETBALL"
-        ]
-    }
-]
-
-

Clone

-

如果想向所有的用户喜好中添加coding。但是又不想影响源数据,那么可以克隆一份数据进行操作。

-
-
json_src.Clone().Select("*.hobby", (p, j) => {
-    j.Append("coding");
-});
-Console.WriteLine(json_src.ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tom",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "cooking", "sing"
-        ]
-    },
-    ...
-]
-
-

数据聚合

-

STJson中内置了部分扩展函数用于聚合操作,进行一些简单的数据处理。结合STJsonPath可以快速且简单的完成一些数据操作。

-

sort

-

Sort()用于对数据进行排序,内部采用归并排序方式。此函数有如下形式签名:

-
- - - - - -
returnsignaturenote
STJsonSort()不指定path进行排序。默认升序。
STJsonSort(bool)不指定path进行排序,指定是否为降序。
STJsonSort(params object[])用于指定path或是否降序。
-
-

不指定path进行排序:

-
-
var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } };
-var json_objs = STJson.FromObject(arr_obj);
-Console.WriteLine(json_objs.Sort());
-Console.WriteLine(json_objs.Sort(true));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[true,1,2,4,5,6,{"aa":"aa"},null]
-[6,5,4,2,true,1,{"aa":"aa"},null]
-
-

你是否会感到疑惑为什么会是这样的结果?。。。但是在那之前为什么你不先疑惑一下为什么要用这样的数组去排序呢?

-

Sort的内部会对每个元素获取出一个数字并记录元素索引,然后对这个数字进行排序,最后根据索引位置重新组合一个数组达到排序目的。对于如何获取元素的数字规则如下:

-
-
switch (item.ValueType) {
-    case STJsonValueType.Long:
-    case STJsonValueType.Double:
-        d_temp = item.GetValue<double>();
-        break;
-    case STJsonValueType.Boolean: // true 被计算成 1
-        d_temp = item.GetValue<bool>() ? 1 : 0;
-        break;
-    case STJsonValueType.Datetime:
-        d_temp = Convert.ToDouble(item.GetValue<DateTime>());
-        break;
-    case STJsonValueType.String:
-        d_temp = item.Value == null ? 0 : item.Value.ToString().Length;
-        break;
-    default:    // 对于其他类型让其始终排在最后面即可
-        d_temp = arr_b_desc[j] ? double.MinValue : double.MaxValue;
-        break;
-}
-
-

所以如果你没有那么奇怪的数组的话,那么一切都会正常起来。如果实在是想只对里面的数字排序怎么办?

-
-
var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } };
-var json_objs = STJson.FromObject(arr_obj)
-    .Select("..[?(typeof(@) in ['long', 'double'])]");
-Console.WriteLine(json_objs.Sort());
-Console.WriteLine(json_objs.Sort(true));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[1,2,4,5,6]
-[6,5,4,2,1]
-
-

指定path进行排序:

-
-
Console.WriteLine(json_src.Sort("age").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tony",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "game", "dance"
-        ]
-    }, {
-        "name": "Tom",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "cooking", "sing"
-        ]
-    }, {
-        "name": "Andy",
-        "age": 20,
-        "gender": 1,
-        "hobby": [
-            "draw", "sing"
-        ]
-    }, {
-        "name": "Kun",
-        "age": 26,
-        "gender": 1,
-        "hobby": [
-            "sing", "dance", "rap", "basketball"
-        ]
-    }
-]
-
-

当然你也可以指定降序排列:

-
-
Console.WriteLine(json_src.Sort("age", true).ToString(4));
-
-

还可以同时指定多个字段进行排序:

-
-
Console.WriteLine(json_src.Sort("gender", false, "age", true).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    {
-        "name": "Tony",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "game", "dance"
-        ]
-    }, {
-        "name": "Tom",
-        "age": 16,
-        "gender": 0,
-        "hobby": [
-            "cooking", "sing"
-        ]
-    }, {
-        "name": "Kun",
-        "age": 26,
-        "gender": 1,
-        "hobby": [
-            "sing", "dance", "rap", "basketball"
-        ]
-    }, {
-        "name": "Andy",
-        "age": 20,
-        "gender": 1,
-        "hobby": [
-            "draw", "sing"
-        ]
-    }
-]
-
-

以上为使用gender进行升序,然后对于相同值再对age进行降序排列。Sort函数参数规则如下:

-
-
Sort(string strJsonPath, bool isDesc[, string strJsonPath, bool isDesc]*);
-// STLib.Json.STJsonExtension.cs:Sort()
-public static STJson Sort(this STJson json, params object[] fields) {
-    var jsonSort = STJson.CreateArray();
-    for (int i = 0; i < fields.Length; i += 2) {
-        var j = new STJson()
-            .SetItem("path", fields[i] == null ? null : fields[i].ToString())
-            .SetItem("desc", i + 1 < fields.Length ? Convert.ToBoolean(fields[i + 1]) : false);
-        jsonSort.Append(j);
-    }
-    return json.Sort(jsonSort);
-}
-
-

group

-

Group()用于对数据指定path进行分组。使用方式如下:

-
-
Console.WriteLine(json_src.Group("gender").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "gender": [
-        {
-            "value": 0,
-            "items": [
-                {
-                    "name": "Tom",
-                    "age": 16,
-                    "gender": 0,
-                    "hobby": [
-                        "cooking", "sing"
-                    ]
-                }, {
-                    "name": "Tony",
-                    "age": 16,
-                    "gender": 0,
-                    "hobby": [
-                        "game", "dance"
-                    ]
-                }
-            ]
-        }, {
-            "value": 1,
-            "items": [
-                {
-                    "name": "Andy",
-                    "age": 20,
-                    "gender": 1,
-                    "hobby": [
-                        "draw", "sing"
-                    ]
-                }, {
-                    "name": "Kun",
-                    "age": 26,
-                    "gender": 1,
-                    "hobby": [
-                        "sing", "dance", "rap", "basketball"
-                    ]
-                }
-            ]
-        }
-    ]
-
-

同样的你也可以使用多个path进行分组,但是和Sort不同的是这些path不是对第一个path的结果再进行分组。是并列的,而非嵌套的。

-
-
Console.WriteLine(json_src.Group("gender", "age").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "gender": [...],
-    "age": [...]
-}
-
-

如果想进行嵌套Group()应该怎么办?作者并不打算在Group()中实现,因为STJsonPath可以完成这个工作。

-

terms

-

Terms()ElasticSearch数据库中的聚合类似,统计某个字段的出现次数,与Sort()一样,它可以选择是否指定path或者多个path

-
-
Console.WriteLine(json_src.Terms("hobby", "gender").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "hobby": [
-        {
-            "value": "cooking",
-            "count": 1
-        }, {
-            "value": "sing",
-            "count": 3
-        }, {
-            "value": "game",
-            "count": 1
-        }, {
-            "value": "dance",
-            "count": 2
-        }, {
-            "value": "draw",
-            "count": 1
-        }, {
-            "value": "rap",
-            "count": 1
-        }, {
-            "value": "basketball",
-            "count": 1
-        }
-    ],
-    "gender": [
-        {
-            "value": 0,
-            "count": 2
-        }, {
-            "value": 1,
-            "count": 2
-        }
-    ]
-}
-
-

如你所见,似乎hobby并没有对count进行排序啊?是的。。。这是作者故意的。。。咋地?不服?。。。有没有可能其实Terms()还有一个作用?在ES数据库中有个cardinality。用作统计某字段去重后的数据个数。

-

有没有可能其实作者也想实现一个这样的函数?。。但是这个函数写到一半的时候,作者当场Delete。。。没必要啊。。上面的Terms()不就已经完成了这个工作了吗?比如:

-
-
json_src.Terms("hobby")["hobby"].Count;
-
-

而且有没有一种可能cardinality只统计个数。。。但是。。如果想要获取到去重后的字段值都有哪些要怎么办?。。。而刚才说的这些情况Terms()似乎都已经完成了。。仅仅是没有做排序。都已经有Sort()函数了,再单独排个序怎么了?

-
-
json_src.Terms("hobby")["hobby"].Sort("count", true);
-
-

如果想获得人员最受欢迎的前三个喜好要怎么做?

-
-
Console.WriteLine(
-    json_src.Terms("hobby")["hobby"]
-    .Sort("count", true)
-    .Select("[0:3]")
-    .Select("*.value")
-    .ToString(4)
-    );
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-[
-    "sing", "dance", "basketball"
-]
-
-

min, max

-

Min() Max()分别为寻找最小值和最大值元素,可以不指定或者指定多个path

-
-
Console.WriteLine(json_src.Min("age").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "age": {
-        "count": 4,                 // 其中 4 个元素参与了计算
-        "value": 16,                // 最小的 age 值为 16
-        "items": [                  // 满足最小值的元素
-            {
-                "name": "Tom",
-                "age": 16,
-                "gender": 0,
-                "hobby": [
-                    "cooking", "sing"
-                ]
-            }, {
-                "name": "Tony",
-                "age": 16,
-                "gender": 0,
-                "hobby": [
-                    "game", "dance"
-                ]
-            }
-        ]
-    }
-}
-
-

其实上面的效果有点像Group有点像了,再加上一个Sort()。。简直就是一模一样。。。连作者自己都懵逼了。。。就应该在Min/Max()内部使用Group() + Sort()实现的。。算了算了。。代码都写完了。但是很显然Sort()必然会降低效率。。但是话又说回来。。小数据量不在乎效率。。但是大数据量呢???大数据量你给我说你用Json数组保存????你食不食油饼???

-

avg, sum

-

Avg() Sum()分别用于计算平均值和汇总,可以不指定或者指定多个key

-
-
Console.WriteLine(json_src.Avg("age").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "age": {
-        "count": 4,         // 其中 4 个元素参与了计算
-        "value": 19.5       // 平均值 19.5
-    }
-}
-
-

Sum()返回值与Avg()一致,唯一不同的是,value一个是平均值,一个是汇总。

-

STJsonPath.Name

-

在上面的演示中我们有使用指定一个字段进行数据操作,在结果的json中会以这个字段名称作为一个key输出。但是上面并没有强调是在指定key进行操作,而是path。只是路径比较简单让我们看起来像是key

-

如果这样进行聚合呢?

-
-
Console.WriteLine(json_src.Terms("hobby[0]").ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "hobby[0]": [
-        {
-            "value": "cooking",
-            "count": 1
-        }, {
-            "value": "game",
-            "count": 1
-        }, {
-            "value": "draw",
-            "count": 1
-        }, {
-            "value": "sing",
-            "count": 1
-        }
-    ]
-}
-
-

好像看上去很奇怪的样子。其实STJsonPath有一个Name属性。

-
-
Console.WriteLine(json_src.Terms(new STJsonPath("test", "hobby[0]")).ToString(4));
-/*******************************************************************************
- *                                [output]                                     *
- *******************************************************************************/
-{
-    "test": [
-        {
-            "value": "cooking",
-            "count": 1
-        }, {
-            "value": "game",
-            "count": 1
-        }, {
-            "value": "draw",
-            "count": 1
-        }, {
-            "value": "sing",
-            "count": 1
-        }
-    ]
-}
-
-

THE END

-

非常感谢看到最后,作者写的都快要疯掉了,一半的时候就想摆烂放弃了。咬咬牙终于完成了。如果在使用过程中遇到什么问题请一定联系作者,作者一定在第一时间用小本本记录下来。至于改不改。。。日后再说。。

-
-
    -
  • TG: DebugST
  • -
  • QQ: 2212233137
  • -
  • Mail: 2212233137@qq.com
  • -
-
- -
- -
- - + + + + + +STJson + + + + + + +
+ + +
+

STLib.Json

+

简介

+

STLib.Json是一款基于MIT开源协议的Json解析库。由DebugST开发,该库纯原生实现不依赖任何库,所以非常轻量便捷,且功能强大。由于没用任何依赖纯原生构建,它可以很容易移植到其他编程语言中。当然由于作者懒的一批,这个概率不是很大,做做梦就好了不要太当真。

+

那么为什么要开发STJson呢?众所周知,.NET自带的解析库用户体验过于友好,想必大家肯定不太习惯使用。而且刚好作者目前也总是需要使用到JSON数据的处理。虽然有第三方的一些库可以使用,但是在作者体验后还是觉得,算了自己动手吧。主要是两点

+
    +
  • JSON5 支持不够友好
  • +
  • JsonPath 支持不够友好
  • +
+

在设计STJson的时候作者没有任何的异议,毕竟Json的数据格式不存在有争议的地方,当然在兼容JSON5的时候作者还是加入部分自己的想法,反正目前的JSON5也并不是正式的文档。

+

当在设计STJsonPath的时候,作者犹豫了,因为作者发现很多存在争议的地方,且无法找到关于JsonPath的正式RFC文档,仅仅找到一份草案:

+

JSONPath: Query expressions for JSON

+

虽然草案中一些疑惑也能得到解决,但是作者仍有一些疑惑,毕竟不是正式的文档。在开发之前作者也好奇其他JsonPath的开源库,对于有争议的地方其他库是如何解决的。但很遗憾,问题依然存在。似乎没有库愿意搭理这份草案。既然JsonPath还没有正式的RFC文档,只能说作者会尽可能兼容草案的同时,还会加入一些作者自己的想法。

+

路劲疑问

+

JsonPath是源自于XPath的使用方式,总所周知XPathXML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。显而易见JsonPath是用于Json的。它们两种几乎使用方式相同,但是XMLJson两种数据格式的差异,必然会有存在不同的地方。

+

比如在XML的元素名中是不存在特殊字符的,可是JsonKey可以是任意的字符串,假如有一下Json数据:

+
+
{ "aa": { "bb": "cc" } }
+
+

JsonPath中我们可以通过路径aa.bb获取到值cc,这没有任何问题,如果Json数据换成下面的呢:

+
+
{ "a.a": { "bb": "cc" } }
+
+

很显然通过a.a.bb是无法得到值cc的。或许通常情况下Json都表示某个对象,而对象是不会存在这么奇怪的属性的。但是对于Json的数据格式而言,它的Key可以是任意字符。即便在浏览器中通过{OBJ_NAME}.a.a.bb也是只会得到报错。但是浏览器访问Json元素的方式并不是只用通过.的方式去获取,还有索引器{OBJ_NAME}['a.a']['bb']是可以得到正确值的。

+

STJsonPath中允许使用'或者",在词法分析器(STJsonPathTokenizer.cs)中会将其标记为String,若此字符串在非表达式的作用域内,解析器(STJsonPathParser.cs)会将其重新标记为Property作为索引使用,从而避免特殊字符串无法处理的情况,字符串支持\进行转义。所以在STJsonPath中可以使用'a.a'.bb获取到正确值。而在草案中也确实提到过类似的处理方式。

+

表达式疑问

+

在草案中有提到支持表达式,且表达式有两种类型,()?()。分别代表普通表达式过滤表达式。而按照作者的理解,普通表达式用于计算出一个值,且将这个值作为JsonPath的一部分,比如在很多案例中可以看到的$.books[(@.length - 1)]。在执行JsonPath的时候会可能对Json元素进行层层递归,其中@是一个动态变量,表示递归过程中当前正在处理的Json元素,根据表达式的表面意思是想获取books中的倒数第一个元素。

+

但是@.length如何被执行?length是哪里来的?如果@是一个数组,作者暂且可以理解为是在对数组求长度?可是如果@是一个对象呢?

+

既然是表达式,那么可以在()中写什么样的语法呢?语法规呢?关于这些疑问作者都采用了自己的实现方式,将在稍后的教程中介绍。

+

STJson API

+

数据类型关系

+
+ + + + + + + + + + + + + + + + +
.NetSTJsonValueType.NetSTJsonValueType
byteLongsbyteLong
shortLongushortLong
intLonguintLong
longLongulongLong
floatDoubledoubleDouble
decimalDoubleboolBoolean
charStringstringString
DateTimeStringenumLong or String
PointArrayPointFArray
SizeArraySizeFArray
RectangleArrayRectangleFArray
ColorArrayDataTableObject
ArrayArrayICollectionArray
IDectionaryObjectobjectObject
+
+

STJson涵盖了常见的基本数据类型,即便不包含在其中,那么最后也会通过执行反射递归对象的属性。

+

静态函数

+
+

下列中(+n)表示有多个重载

+
+
+ + + + + + + + + + + + + + +
returnsignaturenote
stringSerialize(+n)object对象序列化为字符串。
STJsonDeserialize(string)将字符串转换为STJson对象序。
TDeserialize<T>(+n)将对象或字符串转换为目标对象。
STJsonWriterWrite创建一个STJsonWriter对象。
STJsonReaderRead创建一个STJsonReader对象。
STJsonCreate(STJsonCreator)创建一个Json对象。
STJsonSTJson CreateObject()创建一个空白对象。
STJsonSTJson CreateArray(params object[])创建一个数组对象。
STJsonFromObject(+n)将一个对象转换为STJson
stringFormat(+n)格式化一个Json字符串。
voidAddCustomConverter(+n)自定义类型转换器。
voidRemoveCustomConverter(+n)移除自定义转换器。
+
+

非静态函数

+
+ + + + + + + + + + + + + +
returnsignaturenote
STJsonSetItem(+n)向对象中添加一个键值对,并返回自己。
STJsonSetKey(string)向对象中添加一个key,并返回目标对象。
voidGetValue(+n)获取目标对象的值。
voidSetValue(+n)设置目标对象的值。
STJsonDelete(string)从对象中移除一个key,并返回目标对象。
STJsonAppend(+n)向数组对象中添加一个或一些元素,并返回自己。
STJsonInsert(+n)向数组对象中插入一个元素,并返回自己。
STJsonRemoveAt(int nIndex)从数组对象中删除一个索引。
voidClear()清空所有子元素。
IEnumerator<STJson>GetEnumerator()获取当前元素中所有子元素。
STJsonClone克隆当前元素。
+
+

扩展函数

+
+ + + + + + + + + + + + + + + + +
returnsignaturenote
boolIsNullOrNullValue判断当前对象是否为空或者空值。
STJsonSet(+n)根据路径(STJsonPath)设置对象。
stringGetValue(+n)获取对象的字符串值(仅值类型)
TGetValue<T>(+n)获取对象值(仅值类型)
STJsonSelect(+n)在对象中进行数据筛选。
STJsonSelectFirst(+n)在对象中进行数据筛选,并选中第一个结果。
STJsonSelectLast(+n)在对象中进行数据筛选,并选中最后一个结果。
STJsonGroup(+n)对指定字段进行分组。
STJsonTerms(+n)对指定字段的值进行个数统计。
STJsonSort(+n)对指定字段进行排序处理。
STJsonMin(+n)统计指定字段的最小值。
STJsonMax(+n)统计指定字段的最大值。
STJsonSum(+n)统计指定字段的总数。
STJsonAvg(+n)统计指定字段的平均值。
+
+

字段

+
+ + + + + + + +
typenamenote
stringKey当前STJson父元素的Key
objectValue当前STJson的值,若当前STJson不是值类型则为null
boolIsNullValue当前STJson是否为空元素,即无法确定当前STJson值的数据类型。
intCount当前STJson所包含子元素的个数。
STJsonValueTypeValueType(枚举)当前元素的数据类型。
+
+

STJsonValueType为以下值:

+

Undefined String Boolean Long Double Datetime Array Object

+

索引器

+
    +
  • STJson STJson[int]
  • +
  • STJson STJson[string]
  • +
+

其他对象

+
+ + + + + +
名称note
STJsonCreator用于创建一个复杂STJson对象
STJsonReader用于从一个TextReader中动态解析对象。通常用于从文件中读取一个较大数据的Json
STJsonWriter用于向一个TextWriter中写入Json字符串。通常用于直接向文件中写入一个较大切复杂的Json数据。
+
+

STJson [基本应用]

+

STJson是一个中间数据类型,它是stringobject之间的桥梁,使用非常便捷,比如:

+
+
var st_json = new STJson()
+    .SetItem("number", 0)               // 函数返回自身 所以可以连续操作
+    .SetItem("boolean", true)
+    .SetItem("string", "this is string")
+    .SetItem("datetime", DateTime.Now)
+    .SetItem("array_1", STJson.CreateArray(123, true, "string"))
+    .SetItem("array_2", STJson.FromObject(new object[] { 123, true, "string" }))
+    .SetItem("object", new { key = "this is a object" })
+    .SetItem("null", obj: null);
+st_json.SetKey("key").SetValue("this is a test");
+Console.WriteLine(st_json.ToString(4)); // 4 -> indentation space count
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "number": 0,
+    "boolean": true,
+    "string": "this is string",
+    "datetime": "2023-04-22T21:12:30.6109410+08:00",
+    "array_1": [
+        123, true, "string"
+    ],
+    "array_2": [
+        123, true, "string"
+    ],
+    "object": {
+        "key": "this is a object"
+    },
+    "null": null,
+    "key": "this is a test"
+}
+
+
+

当执行var st_json = new STJson()时,st_json为空元素,即st_json.IsNullValue = true。因为此时无法确定st_json对象还是数组或者是

+

STJson中不存在类似于JArrayJObject对象,STJson既可以是Array也可以是ObjectSTJson拥有两个索引器[int][string]

+
+
+
var json_1 = new STJson();
+Console.WriteLine("[json_1] - " + json_1.IsNullValue + " - " + json_1.ValueType);
+
+var json_2 = new STJson();
+json_2.SetItem("key", "value");
+Console.WriteLine("[json_2] - " + json_2.IsNullValue + " - " + json_2.ValueType);
+
+var json_3 = new STJson();
+json_3.Append(1, 2, 3);
+Console.WriteLine("[json_3] - " + json_3.IsNullValue + " - " + json_3.ValueType);
+
+var json_4 = new STJson();
+json_4.SetValue(DateTime.Now);
+Console.WriteLine("[json_4] - " + json_4.IsNullValue + " - " + json_4.ValueType);
+
+var json_5 = STJson.CreateArray();          // made by static function
+Console.WriteLine("[json_5] - " + json_5.IsNullValue + " - " + json_5.ValueType);
+
+var json_6 = STJson.CreateObject();         // made by static function
+Console.WriteLine("[json_6] - " + json_6.IsNullValue + " - " + json_6.ValueType);
+
+var json_7 = STJson.FromObject(12);         // made by static function
+Console.WriteLine("[json_3] - " + json_7.IsNullValue + " - " + json_7.ValueType);
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[json_1] - True - Undefined
+[json_2] - False - Object
+[json_3] - False - Array
+[json_4] - False - Datetime
+[json_5] - False - Array
+[json_6] - False - Object
+[json_7] - False - Long
+
+
+

通常情况下SetItem(+n)会将ValueType设置为Object,而Append(+n)Insert(+n)会将ValueType设置为Array

+
+

正如上面提到的STJson有两个索引器,可以通过索引器访问他们,或者获取值。

+
+
var json_temp = STJson.CreateArray()
+    .SetItem("string", "this is string")
+    .SetItem("array", new Object[] { "1", "2", "3" });
+Console.WriteLine(json_temp["string"]);
+Console.WriteLine(json_temp["string"].GetValue());
+Console.WriteLine(json_temp["array"][1]);
+Console.WriteLine(json_temp["array"][1].GetValue<long>());
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+"this is string"
+this is string
+"2"
+2
+
+

object -> string

+

通过上面的例子或许你已经知道怎么将一个对象转换为string,通过STJson.FromObject(object).ToString(+n)即可,但是有没有可能,其实不用这么麻烦的?比如:STJson.Serialize(+n)就可以了???

+

事实上STJson.Serialize(+n)的效率会更好,因为它是直接将对象转换为字符串,而不是转换成STJson再转换成字符串。

+
+
Console.WriteLine(STJson.Serialize(new { key = "this is test" }));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{"key":"this is test"}
+
+

当然你可以有个更友好的输出格式:

+
+
Console.WriteLine(STJson.Serialize(new { key = "this is test" }, 4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "key": "this is test"
+}
+
+

或者你可以直接序列化到一个TextWriter中去:

+
+
STJson.Serialize(new { key = "this is test" }, new StreamWriter("./test.json")));
+
+

string -> object

+

事实上代码并不会直接将string转换为object。因为在那之前必须先对字符串进行解析,确保它是一个正确格式的Json。但是做完这个过程的时候已经得到一个STJson对象了。最后将STJson再转换为object

+

所以你会在源代码STLib.Json.Converter中看到如下文件:

+

ObjectToSTJson.cs ObjectToString.cs STJsonToObject.cs

+

里面并没有StringToObject.cs文件,而STJson.Deserialize(+n)的源码如下:

+
+
public static T Deserialize<T>(string strJson, +n) {
+    var json = STJsonParser.Parse(strJson);
+    return STJsonToObject.Get<T>(json, +n);
+}
+
+

STJson -> object

+

如何将字符串转换为对象,相信作者不用说明读者也应该知道如何处理,但是这里值得说明的是,STJson可以附加到对象中,实现局部更新。

+
+
public class TestClass {
+    public int X;
+    public int Y;
+}
+
+TestClass tc = new TestClass() {
+    X = 10,
+    Y = 20
+};
+STJson json_test = new STJson().SetItem("Y", 100);
+STJson.Deserialize(json_test, tc);
+Console.WriteLine(STJson.Serialize(tc));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+ {"X":10,"Y":100}
+
+

STJsonConverter

+

虽然在STJson中内置了很多数据类型的转换,即便没有的数据类型也会被当做object做递归处理。但是有时情况并不是很友好。比如:

+
+
Rectangle rect = new Rectangle(10, 10, 100, 100);
+Console.WriteLine(STJson.Serialize(rect, 4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "Location": {
+        "IsEmpty": false,
+        "X": 10,
+        "Y": 10
+    },
+    "Size": {
+        "IsEmpty": false,
+        "Width": 100,
+        "Height": 100
+    },
+    "X": 10,
+    "Y": 10,
+    "Width": 100,
+    "Height": 100,
+    "Left": 10,
+    "Top": 10,
+    "Right": 110,
+    "Bottom": 110,
+    "IsEmpty": false
+}
+
+

很显然,这个结果过于复杂,因为Rectangle的所有字段都被递归出来了。但是,如果这样呢?

+
+
public class RectangleConverter : STJsonConverter
+{
+    public override object JsonToObject(Type t, STJson json, ref bool bProcessed) {
+        return new Rectangle(
+            json["x"].GetValue<int>(),
+            json["y"].GetValue<int>(),
+            json["w"].GetValue<int>(),
+            json["h"].GetValue<int>());
+    }
+
+    public override STJson ObjectToJson(Type t, object obj, ref bool bProcessed) {
+        Rectangle rect = (Rectangle)obj;
+        return STJson.New()
+            .SetItem("x", rect.X)
+            .SetItem("y", rect.Y)
+            .SetItem("w", rect.Width)
+            .SetItem("h", rect.Height);
+    }
+
+    public override string ObjectToString(Type t, object obj, ref bool bProcessed) {
+        //return "{\"x\":" + ... + "}"
+        var json = this.ObjectToJson(t, obj, ref bProcessed);
+        if (bProcessed) {
+            return json.ToString();
+        }
+        return null;
+    }
+}
+
+Rectangle rect = new Rectangle(10, 10, 100, 100);
+STJson.AddCustomConverter(typeof(Rectangle), new RectangleConverter());
+string strResult = STJson.Serialize(rect);
+Console.WriteLine(strResult);
+rect = STJson.Deserialize<Rectangle>(strResult.Replace("100", "200"));
+Console.WriteLine(rect);
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{"x": 10,"y": 10,"w": 100,"h": 100}
+{X=10,Y=10,Width=200,Height=200}
+
+
+

其中bProcessed默认传入值为true,当上一层函数获取到false时则使用默认的处理方式。

+
+

STJsonConverter提供了Attribute类,也可用于标记对象属性。

+
+
public class Test{
+    [STJsonConverter(typeof(RectangleConverter))]
+    public Rectangle Rect{get; set;}
+}
+
+STJsonConverter.cs +
+
public abstract class STJsonConverter
+{
+    public virtual STJson ObjectToJson(Type t, object obj, ref bool bProcessed) {
+        bProcessed = false;
+        return null;
+    }
+    public virtual string ObjectToString(Type t, object obj, ref bool bProcessed) {
+        bProcessed = false;
+        return null;
+    }
+    public virtual object JsonToObject(Type t, STJson json, ref bool bProcessed) {
+        bProcessed = false;
+        return null;
+    }
+}
+
+

STJsonAttribute

+

或许在序列化的时候你并不想输出所有的属性,那么可以通过STJsonAttribute去控制。

+
+
[STJson(STJsonSerilizaMode.Include)]    // optional
+public class Student
+{
+    [STJsonProperty("test_name")]
+    public string Name;
+    public int Age;
+    public Gender Gender;
+    [STJsonProperty]                        // optional
+    public List<string> Hobby;
+}
+
+public enum Gender
+{
+    Male, Female
+}
+
+var stu = new Student() {
+    Name = "Tom",
+    Age = 100,
+    Gender = Gender.Male,
+    Hobby = new List<string>() { "Game", "Sing" }
+};
+
+str = STJson.Serialize(stu);
+Console.WriteLine(str);
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{"test_name":"Tom","Hobby":["Cooking","Sports"]}
+
+

STJsonSetting

+

STJsonSetting用于在序列化或者反序列化中添加一些个性化设置。原本设置是全局的。但随着一些设置项的增多,作者认为全局设置粘性太高,所以独立出来STJsonSetting用于解耦。同时独立的设置类也可以方便后续版本的功能扩展,当然后续版本的概率不是很大。除非作者不想当咸鱼了。

+
+
var stu = new Student() {
+    Name = "Tom",
+    Age = 100,
+    Gender = Gender.Male,
+    Hobby = new List<string>() { "Game", "Sing" }
+};
+STJsonSetting setting = new STJsonSetting();
+setting.EnumUseNumber = true;
+setting.IgnoreAttribute = true;
+setting.Mode = STJsonSettingKeyMode.Exclude;
+setting.KeyList.Add("Age");
+str = STJson.Serialize(stu, setting);
+Console.WriteLine(STJson.Format(str));
+STJson.Deserialize<Student>(str);
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "test_name": "Tom",
+    "Gender": 0,
+    "Hobby": [
+        "Game", "Sing"
+    ]
+}
+
+
+

Attribute的优先级大于STJsonSetting

+
+

JSON5

+

STJson(3.0)中提供了JSON5的支持,并且作者提供了一些更加便捷的想法,以下列文本为例:

+json5.txt +
+
{
+    "normal_key": "This is a normal key and value.", // 这是一个行注释。
+    /*
+       这是一个块注释。
+    */
+    str_value_1: 'string_1',        // 对于键来说["]并不是必须的。、
+    str_value_2: "string_2",        // 在所有的字符串中你可以选择使用[']或者["]。
+    int_numbers: [                  // 支持16进制数字
+        0x123, -0x123, +123, -123
+    ],
+    'float_numbers':[
+        .123, 123., -.123, +123., +123E-2
+    ],
+    // 字符串续行 -> https://json5.org
+    string_1: "string_1.\

+string_2.\
+    |<- some space",
+
+    // 但是在STJson中你可以直接使用[\r\n]换行,且保留换行符。
+    string_2: "string_1.
+string_2.
+        |<- some space",
+
+    // 甚至这样使用。作者认为JSON5的续行方式不友好。
+    // 通过[\]换行后,新行前面不能有空白,不然会被解析到字符串中。
+    // 所以STJson允许连续字符串,并最终将其合并为一个字符串。
+    string_3:
+            "string_1."
+            "string_2."
+            "\r\nstring_3.",
+    array:[
+        123,true,"string_1.""string_2.","string_3",
+    ],
+}
+
+
+
var str_file = "./json5.txt";
+var str_json = File.ReadAllText(str_file, Encoding.UTF8);
+var json = STJson.Deserialize(str_json);
+Console.WriteLine(json.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "normal_key": "This is a normal key and value.",
+    "str_value_1": "string_1",
+    "str_value_2": "string_2",
+    "int_numbers": [
+        291,
+        -291,
+        123,
+        -123
+    ],
+    "float_numbers": [
+        0.123,
+        123,
+        -0.123,
+        123,
+        1.23
+    ],
+    "string_1": "string_1.string_2.    |<- some space",
+    "string_2": "string_1.\r\nstring_2.\r\n        |<- some space",
+    "string_3": "string_1.string_2.\r\nstring_3.",
+    "array": [
+        123,
+        true,
+        "string_1.string_2.",
+        "string_3"
+    ]
+}
+
+

STJsonReader

+

STJsonReader可从一个TextReader中获取字符并动态解析,通常用于解析大文本数据。如:通过StreamReader从文件中或数据流中加载一个Json数据。

+
+
string str_json = @"
+{
+    name: 'DebugST',
+    language: ['C#', 'JS'],
+    address: {
+        country: 'China',
+        province: 'GuangDong',
+        city: 'ShenZhen'
+    }
+}";
+using (var reader = new STJsonReader(new StringReader(str_json))) {
+    foreach (var v in reader) {
+        //Console.WriteLine(STJson.Serialize(v, 4));
+        Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]");
+    }
+}
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+name: DebugST - [String]
+language: [...] - [Array]
+language[0]: C# - [String]
+language[1]: JS - [String]
+address: {...} - [Object]
+address.country: China - [String]
+address.province: GuangDong - [String]
+address.city: ShenZhen - [String]
+
+

默认情况下STJsonReader递归所有的数据。稍微改一下代码:

+
+
foreach (var v in STJson.Read(new StringReader(str_json))) {
+    if (v.Path == "language") {
+        Console.WriteLine(v.GetSTJson().ToString(4));
+    } else {
+        Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]");
+    }
+}
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+name: DebugST - [String]
+[
+    "C#",
+    "JS"
+]
+address: {...} - [Object]
+address.country: China - [String]
+address.province: GuangDong - [String]
+address.city: ShenZhen - [String]
+
+

GetSTJson()将从当前位置开始向后不停获取数据,直到获取到一个完整的STJson对象停止。并且如你所见,在之后的迭代中,已经跳过了language,因为他被GetSTJson()取走了。

+
+

STJsonReader性能远低于STJson.Deserialize(+n),且暂时没有优化STJsonReader的打算。作者认为STJsonReader仅在特殊场景下使用,而特殊场景不考虑性能。

+
+
+

并且由于STJsonReader采用的是动态解析,而且STJson并非采用的状态机进行解析,作者并不打算做严格的Json数据格式校验。所以下面的代码同样能正常运行,且与上面的str_json等效。

+
+
+
string str_json = @"
+{
+    name 'DebugST'
+    language: ['C#' 'JS'],
+    address: {
+        country: 'China',,,,,,,
+        province: 'GuangDong',,,,
+        city:::::::::::: 'ShenZhen'";
+
+
+
string str_json = @"
+{
+    name: 'DebugST',
+    language: ['C#', 'JS'],
+    address: {
+        country: 'China',,,,,,,
+        province: 'GuangDong',,,,
+        city:::::::::::: 'ShenZhen'
+    }}}}}}}}}}
+}1234567890";
+
+
+

STJsonReader在动态解析时,作者为了方便会忽略:,,这样在读取一个键值对的时候只需要读取两个token即可。且直接将第一个作为key,第二个作为value。部分源码如下:

+
+
+
private STJsonToken GetNextFilteredToken()
+{
+    foreach (var v in m_token_reader) {
+        switch (v.Type) {
+            case STJsonTokenType.KVSplitor:     // :
+            case STJsonTokenType.ItemSplitor:   // ,
+                continue;
+            default:
+                return v;
+        }
+    }
+    return STJsonToken.None;
+}
+
+private STJsonReaderItem GetNextObjectKV()
+{
+    var token_key = this.GetNextFilteredToken();
+    switch (token_key.Type) {
+        case STJsonTokenType.None:
+            return null;
+        case STJsonTokenType.ObjectEnd:
+            this.PopStack();
+            return this.GetNextItem();
+        case STJsonTokenType.Symbol:
+        case STJsonTokenType.String:
+            break;
+        default:
+            throw new STJsonParseException(token_key);
+    }
+    m_current_stack.Key = token_key.Value;
+    var token_val = this.GetNextFilteredToken();
+    if (token_val.Type == STJsonTokenType.None) {
+        return null;
+        //throw new Exception("error");
+    }
+    var item = new STJsonReaderItem(this, token_val)
+    {
+        ParentType = STJsonValueType.Object,
+        Key = token_key.Value,
+        Text = token_val.Value
+    };
+    return this.CheckValueToken(item, token_val);
+}
+
+private STJsonReaderItem GetNextArrayItem(){...}
+
+

STJsonWriter

+

STJsonWriterSTJsonReader起到相反的作用,STJsonWriter可以用于直接向一个TextWriter或数据流实时构造并写入Json字符串。

+
+
StringWriter sw = new StringWriter();
+//using (var writer = new STJsonWriter(sw)) {
+//    writer.StartWithArray((w) =>
+//    {
+//        // writer == w
+//    });
+//}
+STJson.Write(sw, 4).StartWithArray((w) =>
+{
+    for (int i = 0; i < 1; i++) {
+        Console.WriteLine("Level:" + w.Level);
+        w.CreateObject(() =>
+        {
+            Console.WriteLine("Level:" + w.Level);
+            w
+            .SetItem("name", "DebugST")
+            //.SetItem("language", STJson.CreateArray("C#", "JS"))
+            //.SetItem("language", new string[] { "C#", "JS" })
+            .SetArray("language", () =>
+            {
+                Console.WriteLine("Level:" + w.Level);
+                w
+                .Append("C#")
+                .Append("JS");
+            })
+            .SetObject("address", () =>
+            {
+                Console.WriteLine("Level:" + w.Level);
+                w
+                .SetItem("country", "china")
+                .SetItem("province", "GuangDong")
+                .SetItem("city", "ShenZhen");
+            });
+        });
+    }
+});
+Console.WriteLine(sw.ToString());
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+Level:1
+Level:2
+Level:3
+Level:3
+[
+    {
+        "name": "DebugST",
+        "language": [
+            "C#",
+            "JS"
+        ],
+        "address": {
+            "country": "china",
+            "province": "GuangDong",
+            "city": "ShenZhen"
+        }
+    }
+]
+
+
+

STJsonWriter采用层层回调的方式,之所以采用这种方式是为了可以与目标Json的数据结构层次关系保持一致。如上所示,STJsonWriter内部自身管理着回调函数的调用层级关系。

+
+

json_src

+

在接下来的教程中我们会使用到一些测试数据,数据如下:

+test.json +
+
[{
+    "name": "Tom", "age": 16, "gender": 0,
+    "hobby": [
+        "cooking", "sing"
+    ]
+},{
+    "name": "Tony", "age": 16, "gender": 0,
+    "hobby": [
+        "game", "dance"
+    ]
+},{
+    "name": "Andy", "age": 20, "gender": 1,
+    "hobby": [
+        "draw", "sing"
+    ]
+},{
+    "name": "Kun", "age": 26, "gender": 1,
+    "hobby": [
+        "sing", "dance", "rap", "basketball"
+    ]
+}]
+
+

将其加载到程序中:

+
+
var json_src = STJson.Deserialize(System.IO.File.ReadAllText("./test.json"));
+
+
+

之后的案例中出现json_src则为以上对象。

+
+

STJsonPath

+

在源码STJsonExtension.cs中对STJson的功能进行了扩展,里面集成一些STJsonPath的功能。所以在STJson的原始代码中并没有对STJsonPath的依赖,STJson可独立使用。但STJsonPath作为STJson的辅助类,需依赖STJson

+

选择器

+
+ + + + + + + + + + + + +
tokennote
$根节点选择器,可视作代表根节点对象。
@当前元素选择器,在遍历过程中指代当前被遍历的元素。
*通配符,表示可以代表任何一个节点。
.<name>子节点选择器,指定子节点的key
..深度选择器,表示可以是任意路径。
['<name>'(,'<name>')]列表选择器,指定子节点的key集合。
[<number>(,(number))]列表选择器,指定子节点的index集合。
[start:end:step]切片选择器,用于指定索引区间。
[(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果作为索引继续向下选择。
[?(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果转换为布尔值,决定是否继续选择。
+
+

使用方式

+

通过以下方式可以构建一个STJsonPath

+
+
// var jp = new STJsonPath("$[0]name");
+// var jp = new STJsonPath("$[0].name");
+var jp = new STJsonPath("[0]'name'"); // 以上方式均可以使用 $不是必须的
+Console.WriteLine(jp.Select(json_src));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+["Tom"]
+
+

当然在STJson中的扩展函数中已经集成STJsonPath,可以通过下面的方式直接使用:

+
+
// var jp = new STJsonPath("[0].name");
+// Console.WriteLine(json_src.Select(jp));
+Console.WriteLine(json_src.Select("[0].name")); // 内部动态构建 STJsonPath
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+["Tom"]
+
+
+

STJsonPath以数组的方式返回数据,其返回值是STJson而不是List<STJson>STJson也可以是数组对象。

+

$开头对于STJsonPath来说并不是必须的,且内部会移除掉开头的$或者@$@仅在表达式中作为对象的变量使用。

+
+
+

在表达式中选择器的返回值仅返回选中的第一个结果。而不是数组列表,这点将在后面说明。

+
+

STJsonPath中允许使用'或者",比如:'a.b' "a.b" STJsonPath会将其视为一个独立的个体。而不是两个。列如有如下Json

+
+
{
+    "a.b": "this is a test"
+}
+
+

很明显通过Select("a.b")是无法获取到数据的,需要通过Select("'a.b'")

+
+
string strTemp = "{\"a.b\": \"this is a test\"}";
+var json_temp = STJson.Deserialize(strTemp);
+Console.WriteLine(json_temp.Select("a.b"));
+Console.WriteLine(json_temp.Select("'a.b'"));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[]
+["this is a test"]
+
+

在字符串中支持\进行转义:\r\n\t\f\b\a\v\0\x..\u....\.

+

通配符

+

通配符可表示当前层级中的任何一个节点。获取所有人员姓名。

+
+
Console.WriteLine(json_src.Select("*.name").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "Tom", "Tony", "Andy", "Kun"
+]
+
+

深度选择器

+

深度选择器与通配符类似,但深度选择器可以是任意层级。

+
+
Console.WriteLine(json_src.Select("..name").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "Tom", "Tony", "Andy", "Kun"
+]
+
+

列表选择器

+

列表选择器支持intstring两种方式,虽然在上面选择器的表格中列举了两个列表选择器,但是在STJsonPath中只有一个列表选择器,它们可以混合使用,比如下面的使用方式都是合法的:

+
    +
  • [0, 1, 2, 3]
  • +
  • ['key_1', 'key_2', key_3]
  • +
  • [0, 'key_1', 1, 'key_2', 2, key_3]
  • +
+

STJsonPath在内部会自动拆分为两个列表选择器,并判断STJsonValueType决定使用哪个列表选择器。内部实现代码如下:

+
+
case STJsonPathItem.ItemType.List:
+    if (jsonCurrent.ValueType == STJsonValueType.Object) {
+        foreach (var v in item.Keys) {
+            if (jsonCurrent[v] == null) {
+                continue;
+            }
+            // ...
+        }
+    }
+    if (jsonCurrent.ValueType == STJsonValueType.Array) {
+        foreach (var v in item.Indices) {
+            nIndexSliceL = v;
+            if (nIndexSliceL < 0) nIndexSliceL = jsonCurrent.Count + nIndexSliceL;
+            if (nIndexSliceL < 0) continue;
+            if (nIndexSliceL >= jsonCurrent.Count) continue;
+            // ...
+        }
+    }
+    break;
+
+

选择索引为02的元素。

+
+
Console.WriteLine(json_src.Select("[0,2]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Tom",
+        "age": 16,
+        "gender": 0,
+        "hobby": [
+            "cooking", "sing"
+        ]
+    }, {
+        "name": "Andy",
+        "age": 20,
+        "gender": 1,
+        "hobby": [
+            "draw", "sing"
+        ]
+    }
+]
+
+

对于int索引可以使用负数,比如-1则表示获取最后一个元素。当STJsonPath检测到负数时候会执行STJson.Count - n将结果作为索引。

+
+
//Console.WriteLine(json_src.Select("-1").ToString(4));
+Console.WriteLine(json_src.Select("[-1]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Kun",
+        "age": 26,
+        "gender": 1,
+        "hobby": [
+            "sing", "dance", "rap", "basketball"
+        ]
+    }
+]
+
+

切片选择器

+

切片选择器用于在数组中选择一个片段,切片选择器默认值[0:-1:1],在切片选择器内部如下实现:

+
+
case STJsonPathItem.ItemType.Slice:
+    if (jsonCurrent.ValueType != STJsonValueType.Array) {
+        return;
+    }
+    if (nIndexSliceL < 0) nIndexSliceL = jsonCurrent.Count + nIndexSliceL;
+    if (nIndexSliceR < 0) nIndexSliceR = jsonCurrent.Count + nIndexSliceR;
+    if (nIndexSliceL < 0) nIndexSliceL = 0;
+    else if (nIndexSliceL >= jsonCurrent.Count) nIndexSliceL = jsonCurrent.Count - 1;
+    if (nIndexSliceR < 0) nIndexSliceR = 0;
+    else if (nIndexSliceR >= jsonCurrent.Count) nIndexSliceR = jsonCurrent.Count - 1;
+    if (nIndexSliceL > nIndexSliceR) {
+        for (int i = nIndexSliceL; i >= nIndexSliceR; i -= item.Step) {
+            // ...
+        }
+    } else {
+        for (int i = nIndexSliceL; i <= nIndexSliceR; i += item.Step) {
+            // ...
+        }
+    }
+    break;
+
+

所以切片中的三个值等同于for循环中的三个条件,所以原理与效果就不再说明。

+
+ + + + + + +
expressionrangenote
[::]0 <= R <= (OBJ).length - 1等同于*
[5:]5 <= R <= {OBJ}.length - 1从第6个元素开始,获取所有元素
[-1:0]{OBJ}.length - 1 >= R >= 0倒序获取数据
[0::2]0 <= R <= {OBJ}.length - 1顺序获取数据,且间隔一个数据
+
+
+

切片选择器中至少出现一个:step大于0,否则将获得异常。

+
+
+
Console.WriteLine(json_src.Select("[-1:]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Kun",
+        "age": 26,
+        "gender": 1,
+        "hobby": [
+            "sing", "dance", "rap", "basketball"
+        ]
+    }
+]
+
+

表达式

+

[?()]中可支持下列运算符,优先级从上至下依次升高

+
+
    +
  • && ||
  • +
  • < <= > >= == != re
  • +
  • & | << >> ^ ~
  • +
  • + -
  • +
  • * / %
  • +
  • in nin anyof
  • +
  • !
  • +
+
+ + + + + + +
operatornotee.g
re正则表达式[?(@.name re 'un')]
in左边的值或数组包含在右边的数组中[?(@.age in [16,20])]
nin左边的值或数组不包含在右边的数组中[?(@.hobby nin ['sing','draw'])]
anyof左边的值或数组和右边的数组存在交集[?(@.hobby anyof ['sing','draw'])]
+
+

表达式有两种模式:

+
    +
  • [?(<expression>)] - 过滤表达式,用于计算出一个布尔值,确定是否继续匹配。
  • +
  • [(<expression>)] - 普通表达式,用于计算出一个值,并将值作为索引继续匹配。
  • +
+

过滤表达式

+

选中name中包含字母ku的元素:

+
+
//Console.WriteLine(json_src.Select("*.[?(@.name == 'kun')]").ToString(4));
+Console.WriteLine(json_src.Select("*.[?(@.name re '(?i)ku')]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Kun",
+        "age": 26,
+        "gender": 1,
+        "hobby": [
+            "sing", "dance", "rap", "basketball"
+        ]
+    }
+]
+
+

(?i)中的i表示忽略大小写,其正则表达式以.NetRegex为标准。(?...)开头则表示设置匹配模式。至于匹配模式自行查阅相关资料。

+

选中hobby不包含singswing的元素:

+
+
Console.WriteLine(json_src.Select("*.[?(@.hobby nin ['sing','draw'])]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Tony",
+        "age": 16,
+        "gender": 0,
+        "hobby": [
+            "game", "dance"
+        ]
+    }
+]
+
+

普通表达式

+

普通表达式会将结果作为STJsonPath的部分继续匹配。

+
+
Console.WriteLine(json_src.Select("*.[('na' + 'me')]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "Tom", "Tony", "Andy", "Kun"
+]
+
+

[('na' + 'me')]'na' + 'me'的结果为'name',并且会将这个值作为索引,所以上述效果等同于*.name,当然返回值也可以是一个集合。

+
+
Console.WriteLine(json_src.Select("*.[(['na' + 'me', 'age', 0, 1 + 1])]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "Tom", 16, "Tony", 16, "Andy", 20, "Kun", 26
+]
+
+

上面表达式的结算结果值为['name', 'age', 0, 2]。但是很显然02将不会起到任何作用,因为第二层的数据对象并不是一个数组。

+

上面的表达式等同于*.['name', 'age', 0, 2]。如果将上面的换成第三层会得到下面的结果。

+
+
Console.WriteLine(json_src.Select("*.*.[(['na' + 'me', 'age', 0, 1 + 1])]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "cooking", "game", "draw", "sing", "rap"
+]
+
+

可以看到'name''age'对于hobby来说是无效的,因为hobby是一个数组。

+

测试表达式

+

可能读者并不了解表达式在内部是如何被执行了并且会输出什么样的结果,作者提供了一个静态测试函数TestExpression()可用于调试表达式。若有什么不明白的地方测试一下就会看到过程及结果。

+
+
Console.WriteLine(STJsonPath.TestExpression(
+    null,           // [STJson] 用于替代表达式中出现的 $
+    null,           // [STJson] 用于替代表达式中出现的 @
+    "1+2+3"         // 表达式文本
+    ).ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "type": "expression",
+    "parsed": "{1 + 2 + 3}",        // 格式化后的文本 {}表示此部分需要单独执行 如: [1, {1+1}, 3]
+    "polish": [
+        "1", "2", " + ", "3", " + " // 逆波兰方式排列
+    ],
+    "steps": [                      // 执行步骤
+        {
+            "type": "excute",
+            "operator": "+",
+            "get_left_token": {     // 计算操作符左边元素的值,表达式左边也可能是一个表达式
+                "parsed": "1",
+                "type": "value",
+                "result": {
+                    "value_type": "Long",
+                    "text": "1"
+                }
+            },
+            "get_right_token": {
+                "parsed": "2",
+                "type": "value",
+                "result": {
+                    "value_type": "Long",
+                    "text": "2"
+                }
+            },
+            "result": {             // 该步骤执行结果
+                "value_type": "Long",
+                "text": "3"
+            }
+        }, {
+            "type": "excute",
+            "operator": "+",
+            "get_left_token": {     // 此时操作符左边的元素为上一步的计算结果
+                "parsed": "3",
+                "type": "value",
+                "result": {
+                    "value_type": "Long",
+                    "text": "3"
+                }
+            },
+            "get_right_token": {
+                "parsed": "3",
+                "type": "value",
+                "result": {
+                    "value_type": "Long",
+                    "text": "3"
+                }
+            },
+            "result": {
+                "value_type": "Long",
+                "text": "6"
+            }
+        }
+    ],
+    "check_result": {               // 清空波兰表达式数据栈,确定最终输出结果。
+        "parsed": "6",
+        "type": "value",
+        "result": {
+            "value_type": "Long",
+            "text": "6"
+        }
+    },
+    "return": {                     // 最终返回值
+        "value_type": "Long",
+        "text": "6",
+        "bool": true                // 如果用作布尔表达式则转换为 true
+    }
+}
+
+

如果过程不重要,仅仅是想看执行结果。

+
+
Console.WriteLine(STJsonPath.TestExpression(
+    null,           // [STJson] 用于替代表达式中出现的 $
+    null,           // [STJson] 用于替代表达式中出现的 @
+    "1+2+3"         // 表达式文本
+    ).SelectFirst("return").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "return": {                     // 最终返回值
+        "value_type": "Long",
+        "text": "6",
+        "bool": true                // 如果用作布尔表达式则转换为 true
+    }
+}
+
+

内置函数

+
+ + + + + + + + + + + + + + + + + + + + + +
returnsignaturenote
stringtypeof(object)获取数据类型。
stringstr(object)转换为字符串。
stringupper(string)转换为大写。
stringlower(string)转换为小写。
longlen(stringarray)获取字符串或者数组长度。
longlong(stringnumber)转换为整数。
doubledouble(stringnumber)转换为浮点数。
numberabs(number)获取绝对值。
longround(number)四舍五入。
longceil(number)向上取整。
numbermax(array)求最大值。
numbermin(array)求最小值。
numberavg(array)求平均值。
numbersum(array)求总和。
stringtrim(string)裁切字符串两端的指定字符。
stringtrims(string)裁切字符串开始的指定字符。
stringtrime(string)裁切字符串末尾的指定字符。
string[]split(string)拆分字符串。
long or stringtime(+n)获取或格式化时间戳。
+
+

typeof具有下列返回值:

+

string long double boolean array object undefined

+

内置函数列表

+

随着版本的迭代更新(如果可能的话),内置函数可能随时发生变化,通过GetBuildInFunctionList()可以查看当前版本支持的内置函数信息。

+
+
Console.WriteLine(STJsonPath.GetBuildInFunctionList().ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "typeof",
+        "demos": [
+            "(object) -> typeof('abc')", "(array,bool) -> typeof(['abc',123],true)"
+        ]
+    },
+    ...
+]
+
+

读者可以通过TestExpression()按照demos对函数进行测试。

+

选中hobby长度大于2的元素:

+
+
Console.WriteLine(json_src.Select("..[?(len(@.hobby) > 1 + 1)]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Kun",
+        "age": 26,
+        "gender": 1,
+        "hobby": [
+            "sing", "dance", "rap", "basketball"
+        ]
+    }
+]
+
+

自定义函数

+

在为表达式提供内置函数的时候作者也不知道开发者期望内置什么样的函数,随便内置了几个之后干脆不想了。没有什么问题是写代码解决不了的,没错。。。让开发者自己去写。但是开发者要怎么样为STJsonPath提供函数呢?

+

STJsonPath.CustomFunctions是一个静态字典。用于保存开发者自定义的函数。其函数签名如下:

+
+
public delegate STJson STJsonPathCustomFuncHander(STJsonPathExpFuncArg[] args);
+
+public struct STJsonPathExpFuncArg
+{
+    public STJsonPathExpFuncArgType Type;
+    public object Value;
+}
+
+public enum STJsonPathExpFuncArgType
+{
+    Undefined, Number, String, Boolean, Array, Object,
+}
+
+

自定义函数返回值统一STJson,因为STJson可与STJsonPath完美衔接,在STJsonPath可直接使用选择器对自定义函数返回值进行操作。比如下面的案例为STJsonPath添加一个matches函数用于正则表达式操作。

+
+
STJsonPath.CustomFunctions.Add("matches", (objs) => {
+    var json_ret = new STJson();
+    json_ret.SetItem("count", 0);
+    json_ret.SetItem("values", STJson.CreateArray());
+    if (objs.Length != 2) {
+        return json_ret;
+    }
+    var ms = Regex.Matches(objs[0].Value.ToString(), objs[1].Value.ToString());
+    json_ret["count"].SetValue(ms.Count);
+    foreach (Match v in ms) {
+        json_ret["values"].Append(STJson.FromObject(new {
+            success = v.Success,
+            index = v.Index,
+            length = v.Length,
+            value = v.Value
+        }));
+    }
+    return json_ret;
+});
+
+

然后我们需要筛选出hobby中包含两个ao的元素:

+
+
Console.WriteLine(json_src.Select("*.hobby.*[?(matches(@,'a|o').count == 2)]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "cooking", "basketball"
+]
+
+
+

自定义函数优先级高于内置函数,也就是说如果在自定义函数中和内置函数中出现了同名函数,则优先调用自定义函数

+
+

表达式中的选择器

+

在表达式中的选择器仅返回选中的第一个结果,而不是数组列表。

+
+
Console.WriteLine(json_src[0].Select("name").ToString(4));
+Console.WriteLine(STJsonPath.TestExpression(json_src[0], json_src[0], "@.name").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "Tom"
+]
+{
+    "type": "expression",
+    "parsed": "{[@]['name']}",
+    "polish": [
+        "[@]['name']"
+    ],
+    "steps": [
+
+    ],
+    "check_result": {
+        "parsed": "[@]['name']",
+        "type": "selector",
+        "root_json": "{\"name\":\"Tom\",\"age\":16,\"gender\":0,\"hobby\":[\"cooking\",\"sing\"]}",
+        "current_json": "{\"name\":\"Tom\",\"age\":16,\"gender\":0,\"hobby\":[\"cooking\",\"sing\"]}",
+        "selected_json": "[\"Tom\"]",
+        "result": {                     // 返回值将只获取第一个结果
+            "value_type": "String",
+            "text": "Tom"
+        }
+    },
+    "return": {
+        "bool": true,
+        "value_type": "String",
+        "text": "Tom"
+    }
+}
+
+

选择方式

+

普通模式(默认方式):

+
+
Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.ItemOnly).ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "Tom", "Tony", "Andy", "Kun"
+]
+
+

路径模式:

+
+
Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.ItemWithPath).ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "path": [
+            0, "name"
+        ],
+        "item": "Tom"
+    }, {
+        "path": [
+            1, "name"
+        ],
+        "item": "Tony"
+    }, {
+        "path": [
+            2, "name"
+        ],
+        "item": "Andy"
+    }, {
+        "path": [
+            3, "name"
+        ],
+        "item": "Kun"
+    }
+]
+
+

保持结构:

+
+
Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.KeepStructure).ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Tom"
+    }, {
+        "name": "Tony"
+    }, {
+        "name": "Andy"
+    }, {
+        "name": "Kun"
+    }
+]
+
+

ParsedTokens

+

GetParsedTokens()用于获取当前STJsonPath得字符串在内部是如何被解析,且以STJson方式输出。如果你也想编写一个解析器,说不定可以给你提供一些思路。

+
+
Console.WriteLine(
+    new STJsonPath("$..[?(matches(@.name,'u').count == 1)]").GetParsedTokens().ToString(2)
+    );
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+  "type": "entry",
+  "parsed": "[..]{matches({[@]['name']}, {'u'}) == 1}",
+  "items": [
+    {
+      "type": "selector_item",
+      "item_type": "Depth",
+      "value": ".."
+    }, {
+      "type": "expression",
+      "parsed": "{matches({[@]['name']}, {'u'}) == 1}",
+      "items": [
+        {
+          "type": "function",
+          "parsed": "matches({[@]['name']}, {'u'})",
+          "name": "matches",
+          "args": {
+            "parsed": "({[@]['name']}, {'u'})",
+            "items": [
+              {
+                "type": "expression",
+                "parsed": "{[@]['name']}",
+                "items": [
+                  {
+                    "type": "expression_item",
+                    "item_type": "selector",
+                    "items": [
+                      {
+                        "type": "selector_item",
+                        "item_type": "Current",
+                        "value": "@"
+                      }, {
+                        "type": "selector_item",
+                        "item_type": "List",
+                        "value": [
+                          "name"
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }, {
+                "type": "expression",
+                "parsed": "{'u'}",
+                "items": [
+                  {
+                    "type": "expression_item",
+                    "item_type": "string",
+                    "value": "u"
+                  }
+                ]
+              }
+            ]
+          },
+          "selector": {
+            "parsed": "['count']",
+            "items": [
+              {
+                "type": "selector_item",
+                "item_type": "List",
+                "value": [
+                  "count"
+                ]
+              }
+            ]
+          }
+        }, {
+          "type": "expression_item",
+          "item_type": "long",
+          "value": 1
+        }, {
+          "type": "expression_item",
+          "item_type": "operator",
+          "value": "=="
+        }
+      ]
+    }
+  ]
+}
+
+

STJson [高级应用]

+

获取值

+

试想一个场景,作为一个WEB后端服务,需要处理前端提交过来的Json数据,假定我们期望得到以下Json数据:

+
+
{
+    "type": "get_list",
+    "page": {
+        "from": 100,
+        "size": 10
+    },
+    "other": {}
+}
+
+

并且我们已经将上面的数据转化为STJson且命名为json_post。其中fromsize用于翻页功能。那么后台可能会做如下判断:

+
+
if(json_post["page"] == null) { /* do something */ }
+if(json_post["page"]["from"] == null) { /* do something */ }
+if(json_post["page"]["from"].ValueType != STJsonValueType.Long) {
+    /* do something */
+}
+int nFrom = json_post["page"]["from"].GetValue<int>();
+// 或者 直接暴力一点
+int nFrom = 0;
+try{
+    nFrom = json_post["page"]["from"].GetValue<int>();
+}catch{
+    /* do something */
+}
+
+

很显然上面的代码让你抓狂。。。当然为了减少麻烦可以在后端代码中为其创建一个实体对象,然后将Json绑定到实体对象中。。。但是为每个Post过来的数据类型创建一个实体对象是不是也挺麻烦的。但是如果使用下面的代码。

+
+
int nFrom = json_post.GetValue<int>("page.from");
+
+

或许你会疑惑,,如果路径不存在或者from根本不是数字怎么办?emm....上面的代码依然会报错,因为上面的代码内部调用是:

+
+
public static T GetValue<T>(this STJson json, string strJsonPath) {
+    return json.GetValue<T>(new STJsonPath(strJsonPath));
+}
+
+public static T GetValue<T>(this STJson json, STJsonPath jsonPath) {
+    var j = jsonPath.SelectFirst(json);
+    if (j == null) {
+        throw new STJsonPathException("Can not selected a object with path {" + jsonPath.SourceText + "}");
+    }
+    var t = typeof(T);
+    bool bProcessed = true;
+    var convert = STJsonBuildInConverter.Get(t);
+    if (convert != null) {
+        var value = convert.JsonToObject(t, json, ref bProcessed);
+        if (bProcessed) {
+            return (T)value;
+        }
+    }
+    return (T)j.Value;
+}
+
+

因为当不存在元素时候,不知道需要返回什么值,可能读者认为无法获取值,返回一个默认值就好了,的确可以这么设计,可是调用者如何确定返回的值是真实的值还是默认值。无法确定是否异常了。除非强制指定一个默认值。

+
+
int nFrom = json_post.GetValue<int>("page.from", 0);
+
+

如果存在元素且能正常转换则返回元素值,否则返回0

+

或者想知道是否返回的是真实值:

+
+
int nFrom = 0;
+var bFlag = json_src.GetValue<int>("page.from", out nFrom);
+
+
+

若填写的strJsonPath会获取到多个值,则取第一个值。

+
+

设置值

+

如果我们需要手动构造一个Json可以通过STJson.SetItem()来添加元素。可是有些场景这样使用似乎有点麻烦,比如以ElasticSearch数据库的query语法为例。我们需要构造一个下面的数据:

+
+
{
+    "query":{
+        "term":{
+            "field_name": "field_value"
+        }
+    }
+}
+
+

那么按照之前的写法我们需要这样去构造一个STJson对象:

+
+
var json = new STJson()
+    .SetItem("query", new STJson()
+        .SetItem("term", new STJson()
+            .SetItem("field_name", "field_value"))
+        );
+// or
+var json = STJson.FromObject(new {
+    query = new {
+        term = new {
+            field_name = "field_value"
+        }
+    }
+});
+
+

但是还可以这样写代码:

+
+
var json = new STJson().Set("query.term.field_name", "field_value");
+Console.WriteLine(json.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "query":{
+        "term":{
+            "field_name": "field_value"
+        }
+    }
+}
+
+

甚至是这样:

+
+
var json = new STJson().Set("array[0:4].[key_1,key_2]", "value");
+Console.WriteLine(json.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "array": [
+        {
+            "key_1": "value",
+            "key_2": "value"
+        }, {
+            "key_1": "value",
+            "key_2": "value"
+        }, {
+            "key_1": "value",
+            "key_2": "value"
+        }, {
+            "key_1": "value",
+            "key_2": "value"
+        }, {
+            "key_1": "value",
+            "key_2": "value"
+        }
+    ]
+}
+
+
+

Set中仅仅支持列表选择器切片选择器。不支持其他选择器。

+
+

STJsonCreator

+

虽然上面通过Set也可以很容易的构造出一个Json数据,但是对于多层嵌套的数据结构通过Set也能实现,但是代码会变得过于复杂并且无法直观的看到Json数据的结构。STJsonCreatorSTJsonWriter功能类似,不过STJsonCreator是直接创建一个STJson对象,通常用于创建复杂结构的Json对象。依然使用ElasticSearch数据库为例,模拟一个数据检索。

+
+
var str_json_post = @"
+{
+    type: 'student',
+    names: ['DebugST', 'None'],
+}";                                         // 模拟前端提交的请求数据
+var json_post = STJson.Deserialize(str_json_post);
+var json_es_query = STJson.Create((c) =>    // 构造ES检索语法
+{
+    c
+    .SetItem("from", (json_post.GetValue("page", 1) - 1) * json_post.GetValue("size", 10))
+    .SetItem("size", json_post.GetValue("size", 10))
+    .Set("query.bool", () =>
+    {
+        c
+        .Set("filter", () =>
+        {
+            c.Set("term.type", json_post["type"]);
+        })
+        // c.Set(str_path, bool, callback) bool -> if true, create this path.
+        .Set("should", !json_post["names"].IsNullOrNullValue(), () =>
+        {
+            // c.Append(0, 5, 1, (i)=>{ }) => for(int i = 0; i < 5; i+=1) {...}
+            // c.Append(IEnumerable, callback);
+            c.Append(json_post["names"], (item) => // => foreach(var item in json_post) {...}
+            {
+                c.Set("term.name", item);
+            });
+        });
+    });
+});
+Console.WriteLine(json_es_query.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "from": 0,
+    "size": 10,
+    "query": {
+        "bool": {
+            "filter": {
+                "term": {
+                    "type": "student"
+                }
+            },
+            "should": [
+                {
+                    "term": {
+                        "name": "DebugST"
+                    }
+                },
+                {
+                    "term": {
+                        "name": "None"
+                    }
+                }
+            ]
+        }
+    }
+}
+
+

可以看到通过POST过来的请求很容易的就构造出了ES数据库的检索语法,并且Json的数据结构保持了一致。并且在SetAppend中提供了多重重载,可以更加便捷的进行逻辑处理,当然它们仅仅是语法糖。通过iffor也能达到同样的目的。

+

STJsonPath 回调函数

+

通过上面的Set我们是否能够将json_src中每个人的喜好都添加一个coding?。。似乎不太可以。毕竟向hobby中添加数据需要append而不是set

+

当然也并不是没有办法。

+
+
json_src.Select("*.hobby", (arg) => {
+    arg.Json.Append("coding");
+});
+Console.WriteLine(json_src.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Tom",
+        "age": 16,
+        "gender": 0,
+        "hobby": [
+            "cooking", "sing", "coding"
+        ]
+    },
+    ...
+]
+
+

Select中支持两种回调函数,一个需要返回值,另一个不需要返回值,如同上面的。而下面的带返回值。

+
+
var json = json_src.Select("*.hobby.*", (arg) => {
+    return new STJsonPathCallBackResult() {
+        Selected = true,            // 是否将此条 json 添加到结果中
+        arg.Json = new STJson()         // 需要被添加到结果的 json
+            .SetItem("path", arg.Path)
+            .SetItem("item", arg.Json.Value.ToString().ToUpper())
+    };
+});
+Console.WriteLine(json.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "path": [
+            0, "hobby", 0
+        ],
+        "item": "COOKING"
+    },
+    ...
+]
+
+

PathItem

+

STJsonPath中有一个比较特殊的数据结构。

+
+
[
+    {
+        "path": [
+            ...
+        ],
+        "item": ...
+    },
+    ...
+]
+
+

可以通过RestorePathJson()还原其结构。

+
+
var json = json_src.Select("*.hobby.*", (arg) => {
+    return new STJsonPathCallBackResult() {
+        Selected = true,
+        arg.Json = new STJson()
+            .SetItem("path", arg.Path)
+            .SetItem("item", arg.Json.Value.ToString().ToUpper())
+    };
+});
+Console.WriteLine(STJsonPath.RestorePathJson(json).ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "hobby": [
+            "COOKING", "SING"
+        ]
+    }, {
+        "hobby": [
+            "GAME", "DANCE"
+        ]
+    }, {
+        "hobby": [
+            "DRAW", "SING"
+        ]
+    }, {
+        "hobby": [
+            "SING", "DANCE", "RAP", "BASKETBALL"
+        ]
+    }
+]
+
+

Clone

+

如果想向所有的用户喜好中添加coding。但是又不想影响源数据,那么可以克隆一份数据进行操作。

+
+
json_src.Clone().Select("*.hobby", (arg) => {
+    arg.Json.Append("coding");
+});
+Console.WriteLine(json_src.ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Tom",
+        "age": 16,
+        "gender": 0,
+        "hobby": [
+            "cooking", "sing"
+        ]
+    },
+    ...
+]
+
+

数据聚合

+

STJson中内置了部分扩展函数用于聚合操作,进行一些简单的数据处理。结合STJsonPath可以快速且简单的完成一些数据操作。

+

sort

+

Sort()用于对数据进行排序,内部采用归并排序方式,并且Sort有多个重载。但最终版本的Sort签名如下,所有的重载,最终调此函数。

+
+
public static STJson Sort(this STJson json, bool is_new_instance, STJsonSortCallback callback);
+
+
+
var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } };
+var json_objs = STJson.FromObject(arr_obj);
+Console.WriteLine(json_objs.Sort());
+Console.WriteLine(json_objs.Sort(true));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[null,1,2,4,5,6,true,{"aa":"aa"}]
+[{"aa":"aa"},true,6,5,4,2,1,null]
+
+

你是否会感到疑惑为什么会是这样的结果?。。。但是在那之前为什么你不先疑惑一下为什么要用这样的数组去排序呢?如上所示Sort需要一个回调函数,STJson的扩展提供了两个默认的回调函数,它被如下代码构造:

+
+
private static STJsonSortCallback BuildDefaultSortCallback(bool is_desc)
+{
+    return (a, b) =>
+    {
+        int n_ret = 0;
+        if (a.IsNullOrNullValue()) {
+            n_ret = -1;
+        } else if (b.IsNullOrNullValue()) {
+            n_ret = 1;
+        } else if (a.IsNumber && b.IsNumber) {
+            n_ret = Convert.ToDouble(a.Value) < Convert.ToDouble(b.Value) ? -1 : 1;
+        } else if (a.ValueType != b.ValueType) {
+            n_ret = a.ValueType - b.ValueType;
+        } else {
+            switch (a.ValueType) {
+                case STJsonValueType.Boolean:
+                    n_ret = (bool)b.Value ? -1 : 1;
+                    break;
+                case STJsonValueType.Datetime:
+                    n_ret = (DateTime)a.Value < (DateTime)b.Value ? -1 : 1;
+                    break;
+                case STJsonValueType.String:
+                    n_ret = string.Compare(a.Value.ToString(), b.Value.ToString());
+                    break;
+            }
+        }
+        return is_desc ? -n_ret : n_ret;
+    };
+}
+
+

所以如果你没有那么奇怪的数组的话,那么一切都会正常起来。如果实在是想只对里面的数字排序怎么办?

+
+
var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } };
+var json_objs = STJson.FromObject(arr_obj)
+    .Select("..[?(typeof(@) in ['long', 'double'])]");
+Console.WriteLine(json_objs.Sort());
+Console.WriteLine(json_objs.Sort(true));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[1,2,4,5,6]
+[6,5,4,2,1]
+
+

指定path进行排序:

+
+
Console.WriteLine(json_src.Sort("age").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    {
+        "name": "Tony",
+        "age": 16,
+        "gender": 0,
+        "hobby": [
+            "game", "dance"
+        ]
+    }, {
+        "name": "Tom",
+        "age": 16,
+        "gender": 0,
+        "hobby": [
+            "cooking", "sing"
+        ]
+    }, {
+        "name": "Andy",
+        "age": 20,
+        "gender": 1,
+        "hobby": [
+            "draw", "sing"
+        ]
+    }, {
+        "name": "Kun",
+        "age": 26,
+        "gender": 1,
+        "hobby": [
+            "sing", "dance", "rap", "basketball"
+        ]
+    }
+]
+
+

当然你也可以指定降序排列:

+
+
Console.WriteLine(json_src.Sort("age", true).ToString(4));
+
+

group

+

Group()用于对数据指定path进行分组。使用方式如下:

+
+
Console.WriteLine(json_src.Group("gender").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "gender": [
+        {
+            "value": 0,
+            "items": [
+                {
+                    "name": "Tom",
+                    "age": 16,
+                    "gender": 0,
+                    "hobby": [
+                        "cooking", "sing"
+                    ]
+                }, {
+                    "name": "Tony",
+                    "age": 16,
+                    "gender": 0,
+                    "hobby": [
+                        "game", "dance"
+                    ]
+                }
+            ]
+        }, {
+            "value": 1,
+            "items": [
+                {
+                    "name": "Andy",
+                    "age": 20,
+                    "gender": 1,
+                    "hobby": [
+                        "draw", "sing"
+                    ]
+                }, {
+                    "name": "Kun",
+                    "age": 26,
+                    "gender": 1,
+                    "hobby": [
+                        "sing", "dance", "rap", "basketball"
+                    ]
+                }
+            ]
+        }
+    ]
+
+

同样的你也可以使用多个path进行分组,但是和Sort不同的是这些path不是对第一个path的结果再进行分组。是并列的,而非嵌套的。

+
+
Console.WriteLine(json_src.Group("gender", "age").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "gender": [...],
+    "age": [...]
+}
+
+

terms

+

Terms()ElasticSearch数据库中的聚合类似,统计某个字段的出现次数,与Sort()一样,它可以选择是否指定path或者多个path

+
+
Console.WriteLine(json_src.Terms("hobby", "gender").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "hobby": [
+        {
+            "value": "cooking",
+            "count": 1
+        }, {
+            "value": "sing",
+            "count": 3
+        }, {
+            "value": "game",
+            "count": 1
+        }, {
+            "value": "dance",
+            "count": 2
+        }, {
+            "value": "draw",
+            "count": 1
+        }, {
+            "value": "rap",
+            "count": 1
+        }, {
+            "value": "basketball",
+            "count": 1
+        }
+    ],
+    "gender": [
+        {
+            "value": 0,
+            "count": 2
+        }, {
+            "value": 1,
+            "count": 2
+        }
+    ]
+}
+
+

如你所见,似乎hobby并没有对count进行排序啊?是的。。。这是作者故意的。。。咋地?不服?。。。有没有可能其实Terms()还有一个作用?在ES数据库中有个cardinality。用作统计某字段去重后的数据个数。

+

有没有可能其实作者也想实现一个这样的函数?。。但是这个函数写到一半的时候,作者当场Delete。。。没必要啊。。上面的Terms()不就已经完成了这个工作了吗?比如:

+
+
json_src.Terms("hobby")["hobby"].Count;
+
+

而且有没有一种可能cardinality只统计个数。。。但是。。如果想要获取到去重后的字段值都有哪些要怎么办?。。。而刚才说的这些情况Terms()似乎都已经完成了。。仅仅是没有做排序。都已经有Sort()函数了,再单独排个序怎么了?

+
+
json_src.Terms("hobby").ForEach((item)=>item.Sort("count", true));
+
+

如果想获得人员最受欢迎的前三个喜好要怎么做?

+
+
Console.WriteLine(
+    json_src.Terms("hobby")["hobby"]
+    .Sort("count", true)
+    .Select("[0:3]")
+    .Select("*.value")
+    .ToString(4)
+    );
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+[
+    "sing", "dance", "basketball"
+]
+
+

min, max

+

Min() Max()分别为寻找最小值和最大值元素,可以不指定或者指定多个path

+
+
Console.WriteLine(json_src.Min("age").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "age": {
+        "count": 4,                 // 其中 4 个元素参与了计算
+        "value": 16,                // 最小的 age 值为 16
+        "items": [                  // 满足最小值的元素
+            {
+                "name": "Tom",
+                "age": 16,
+                "gender": 0,
+                "hobby": [
+                    "cooking", "sing"
+                ]
+            }, {
+                "name": "Tony",
+                "age": 16,
+                "gender": 0,
+                "hobby": [
+                    "game", "dance"
+                ]
+            }
+        ]
+    }
+}
+
+

其实上面的效果有点像Group有点像了,再加上一个Sort()。。简直就是一模一样。。。连作者自己都懵逼了。。。就应该在Min/Max()内部使用Group() + Sort()实现的。。算了算了。。代码都写完了。但是很显然Sort()必然会降低效率。。但是话又说回来。。小数据量不在乎效率。。但是大数据量呢???大数据量你给我说你用Json数组保存????你食不食油饼???

+

avg, sum

+

Avg() Sum()分别用于计算平均值和汇总,可以不指定或者指定多个key

+
+
Console.WriteLine(json_src.Avg("age").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "age": {
+        "count": 4,         // 其中 4 个元素参与了计算
+        "value": 19.5       // 平均值 19.5
+    }
+}
+
+

Sum()返回值与Avg()一致,唯一不同的是,value一个是平均值,一个是汇总。

+

STJsonPath.Name

+

在上面的演示中我们有使用指定一个字段进行数据操作,在结果的json中会以这个字段名称作为一个key输出。但是上面并没有强调是在指定key进行操作,而是path。只是路径比较简单让我们看起来像是key

+

如果这样进行聚合呢?

+
+
Console.WriteLine(json_src.Terms("hobby[0]").ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "hobby[0]": [
+        {
+            "value": "cooking",
+            "count": 1
+        }, {
+            "value": "game",
+            "count": 1
+        }, {
+            "value": "draw",
+            "count": 1
+        }, {
+            "value": "sing",
+            "count": 1
+        }
+    ]
+}
+
+

好像看上去很奇怪的样子。其实STJsonPath有一个Name属性。

+
+
Console.WriteLine(json_src.Terms(new STJsonPath("test", "hobby[0]")).ToString(4));
+/*******************************************************************************
+ *                                [output]                                     *
+ *******************************************************************************/
+{
+    "test": [
+        {
+            "value": "cooking",
+            "count": 1
+        }, {
+            "value": "game",
+            "count": 1
+        }, {
+            "value": "draw",
+            "count": 1
+        }, {
+            "value": "sing",
+            "count": 1
+        }
+    ]
+}
+
+

THE END

+

非常感谢看到最后,如果在使用过程中遇到什么问题请一定及时联系作者,作者一定在第一时间用小本本记录下来。至于改不改。。。日后再说。。。

+
+
    +
  • TG: DebugST
  • +
  • QQ: 2212233137
  • +
  • Mail: 2212233137@qq.com
  • +
+
+ +
+ +
+ + diff --git a/docs/tutorial_cn.txt b/docs/tutorial_cn.txt index ba2574c..9dab1d9 100644 --- a/docs/tutorial_cn.txt +++ b/docs/tutorial_cn.txt @@ -1,31 +1,49 @@ -#T STLib.STJson +#T STLib.Json #O 简介 -`STLib.STJson`是一款基于`MIT`开源协议的`Json`解析库。由[DebugST](https://github.com/DebugST)开发,该库纯原生实现不依赖任何库,所以非常轻量便捷,且功能强大。由于没用任何依赖纯原生构建,它可以很容易移植到其他编程语言中。当然由于作者懒的一批,这个概率不是很大。 -在设计`STJson`的时候作者没有任何的异议,毕竟`Json`的数据格式不存在有争议的地方,当在设计`STJsonPath`的时候,作者犹豫了,因为作者发现很多存在争议的地方,且无法找到关于`JsonPath`的正式`RFC`文档,仅仅找到一份草案: + +`STLib.Json`是一款基于`MIT`开源协议的`Json`解析库。由[DebugST](https://github.com/DebugST)开发,该库纯原生实现不依赖任何库,所以非常轻量便捷,且功能强大。由于没用任何依赖纯原生构建,它可以很容易移植到其他编程语言中。当然由于作者懒的一批,这个概率不是很大,做做梦就好了不要太当真。 + +那么为什么要开发`STJson`呢?众所周知,`.NET`自带的解析库用户体验过于友好,想必大家肯定不太习惯使用。而且刚好作者目前也总是需要使用到`JSON`数据的处理。虽然有第三方的一些库可以使用,但是在作者体验后还是觉得,算了自己动手吧。主要是两点 + +#US +`JSON5` 支持不够友好 +`JsonPath` 支持不够友好 +#UE + +在设计`STJson`的时候作者没有任何的异议,毕竟`Json`的数据格式不存在有争议的地方,当然在兼容`JSON5`的时候作者还是加入部分自己的想法,反正目前的[JSON5](https://json5.org/)也并不是正式的文档。 + +当在设计`STJsonPath`的时候,作者犹豫了,因为作者发现很多存在争议的地方,且无法找到关于`JsonPath`的正式`RFC`文档,仅仅找到一份草案: [JSONPath: Query expressions for JSON](https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-03.html) -虽然草案中一些疑惑也能得到解决,但是作者仍有一些疑惑,毕竟不是正式的文档。在开发之前作者也好奇其他`JsonPath`的开源库,对于有争议的地方其他库是如何解决的。但很遗憾,问题依然存在。似乎没有库愿意搭理这份草案。既然`JsonPath`还没有正式的`RFC`文档,那么作者也想自立门户。但作者并不会不搭理这份草案,只能说作者会尽可能兼容草案的同时,还会加入一些作者自己的想法。 +虽然草案中一些疑惑也能得到解决,但是作者仍有一些疑惑,毕竟不是正式的文档。在开发之前作者也好奇其他`JsonPath`的开源库,对于有争议的地方其他库是如何解决的。但很遗憾,问题依然存在。似乎没有库愿意搭理这份草案。既然`JsonPath`还没有正式的`RFC`文档,只能说作者会尽可能兼容草案的同时,还会加入一些作者自己的想法。 #O 路劲疑问 + `JsonPath`是源自于`XPath`的使用方式,总所周知`XPath`是`XML`路径语言(XML Path Language),它是一种用来确定`XML`文档中某部分位置的语言。显而易见`JsonPath`是用于`Json`的。它们两种几乎使用方式相同,但是`XML`和`Json`两种数据格式的差异,必然会有存在不同的地方。 比如在`XML`的元素名中是不存在特殊字符的,可是`Json`的`Key`可以是任意的字符串,假如有一下`Json`数据: + #CS cs { "aa": { "bb": "cc" } } #CE + 在`JsonPath`中我们可以通过路径`aa.bb`获取到值`cc`,这没有任何问题,如果`Json`数据换成下面的呢: + #CS cs { "a.a": { "bb": "cc" } } #CE + 很显然通过`a.a.bb`是无法得到值`cc`的。或许通常情况下`Json`都表示某个对象,而对象是不会存在这么奇怪的属性的。但是对于`Json`的数据格式而言,它的`Key`可以是任意字符。即便在浏览器中通过`{OBJ_NAME}.a.a.bb`也是只会得到报错。但是浏览器访问`Json`元素的方式并不是只用通过`.`的方式去获取,还有索引器`{OBJ_NAME}['a.a']['bb']`是可以得到正确值的。 在`STJsonPath`中允许使用`'`或者`"`,在词法分析器(STJsonPathTokenizer.cs)中会将其标记为`String`,若此字符串在非表达式的作用域内,解析器(STJsonPathParser.cs)会将其重新标记为`Property`作为索引使用,从而避免特殊字符串无法处理的情况,字符串支持`\\`进行转义。所以在`STJsonPath`中可以使用`'a.a'.bb`获取到正确值。而在草案中也确实提到过类似的处理方式。 #O 表达式疑问 + 在草案中有提到支持表达式,且表达式有两种类型,`()`和`?()`。分别代表`普通表达式`和`过滤表达式`。而按照作者的理解,`普通表达式`用于计算出一个值,且将这个值作为`JsonPath`的一部分,比如在很多案例中可以看到的`$.books[(@.length - 1)]`。在执行`JsonPath`的时候会可能对`Json`元素进行层层递归,其中`@`是一个动态变量,表示递归过程中当前正在处理的`Json`元素,根据表达式的表面意思是想获取`books`中的倒数第一个元素。 但是`@.length`如何被执行?`length`是哪里来的?如果`@`是一个数组,作者暂且可以理解为是在对数组求长度?可是如果`@`是一个对象呢? 既然是表达式,那么可以在`()`中写什么样的语法呢?语法规呢?关于这些疑问作者都采用了自己的实现方式,将在稍后的教程中介绍。 #T STJson API + #O 数据类型关系 #TS .Net|STJsonValueType|.Net|STJsonValueType @@ -57,6 +75,9 @@ IDectionary|Object|object|Object string|Serialize(+n)|将`object`对象序列化为字符串。 STJson|Deserialize(string)|将字符串转换为`STJson`对象序。 T|Deserialize(+n)|将对象或字符串转换为目标对象。 +STJsonWriter|Write|创建一个STJsonWriter对象。 +STJsonReader|Read|创建一个STJsonReader对象。 +STJson|Create(STJsonCreator)|创建一个`Json`对象。 STJson|STJson CreateObject()|创建一个空白对象。 STJson|STJson CreateArray(params object[])|创建一个数组对象。 STJson|FromObject(+n)|将一个对象转换为`STJson`。 @@ -71,6 +92,7 @@ void|RemoveCustomConverter(+n)|移除自定义转换器。 #TS return|signature|note STJson|SetItem(+n)|向对象中添加一个键值对,并返回自己。 STJson|SetKey(string)|向对象中添加一个`key`,并返回目标对象。 +void|GetValue(+n)|获取目标对象的值。 void|SetValue(+n)|设置目标对象的值。 STJson|Delete(string)|从对象中移除一个`key`,并返回目标对象。 STJson|Append(+n)|向数组对象中添加一个或一些元素,并返回自己。 @@ -84,6 +106,7 @@ STJson|Clone|克隆当前元素。 #O 扩展函数 #TS return|signature|note +bool|IsNullOrNullValue|判断当前对象是否为空或者空值。 STJson|Set(+n)|根据路径(STJsonPath)设置对象。 string|GetValue(+n)|获取对象的字符串值(仅值类型) T|GetValue(+n)|获取对象值(仅值类型) @@ -104,7 +127,7 @@ STJson|Avg(+n)|统计指定字段的平均值。 #TS type|name|note string|Key|当前`STJson`父元素的`Key` object|Value|当前`STJson`的值,若当前`STJson`不是值类型则为`null` -bool|IsNullObject|当前`STJson`是否为空元素,即无法确定当前`STJson`值的数据类型。 +bool|IsNullValue|当前`STJson`是否为空元素,即无法确定当前`STJson`值的数据类型。 int|Count|当前`STJson`所包含子元素的个数。 STJsonValueType|ValueType|(枚举)当前元素的数据类型。 #TE @@ -118,6 +141,14 @@ STJson STJson[int] STJson STJson[string] #UE +#O 其他对象 + +#TS 名称|note +STJsonCreator|用于创建一个复杂`STJson`对象 +STJsonReader|用于从一个`TextReader`中动态解析对象。通常用于从文件中读取一个较大数据的`Json`。 +STJsonWriter|用于向一个`TextWriter`中写入`Json`字符串。通常用于直接向文件中写入一个较大切复杂的`Json`数据。 +#TE + #T STJson [基本应用] `STJson`是一个中间数据类型,它是`string`与`object`之间的桥梁,使用非常便捷,比如: @@ -156,37 +187,37 @@ Console.WriteLine(st_json.ToString(4)); // 4 -> indentation space count #CE #HS pink|black -当执行`var st_json = new STJson()`时,`st_json`为空元素,即`st_json.IsNullObject = true`。因为此时无法确定`st_json`是`对象`还是`数组`或者是`值`。 +当执行`var st_json = new STJson()`时,`st_json`为空元素,即`st_json.IsNullValue = true`。因为此时无法确定`st_json`是`对象`还是`数组`或者是`值`。 在`STJson`中不存在类似于`JArray`或`JObject`对象,`STJson`既可以是`Array`也可以是`Object`。`STJson`拥有两个索引器`[int]``[string]`。 #HE #CS cs var json_1 = new STJson(); -Console.WriteLine("[json_1] - " + json_1.IsNullObject + " - " + json_1.ValueType); +Console.WriteLine("[json_1] - " + json_1.IsNullValue + " - " + json_1.ValueType); var json_2 = new STJson(); json_2.SetItem("key", "value"); -Console.WriteLine("[json_2] - " + json_2.IsNullObject + " - " + json_2.ValueType); +Console.WriteLine("[json_2] - " + json_2.IsNullValue + " - " + json_2.ValueType); var json_3 = new STJson(); json_3.Append(1, 2, 3); -Console.WriteLine("[json_3] - " + json_3.IsNullObject + " - " + json_3.ValueType); +Console.WriteLine("[json_3] - " + json_3.IsNullValue + " - " + json_3.ValueType); var json_4 = new STJson(); json_4.SetValue(DateTime.Now); -Console.WriteLine("[json_4] - " + json_4.IsNullObject + " - " + json_4.ValueType); +Console.WriteLine("[json_4] - " + json_4.IsNullValue + " - " + json_4.ValueType); var json_5 = STJson.CreateArray(); // made by static function -Console.WriteLine("[json_5] - " + json_5.IsNullObject + " - " + json_5.ValueType); +Console.WriteLine("[json_5] - " + json_5.IsNullValue + " - " + json_5.ValueType); var json_6 = STJson.CreateObject(); // made by static function -Console.WriteLine("[json_6] - " + json_6.IsNullObject + " - " + json_6.ValueType); +Console.WriteLine("[json_6] - " + json_6.IsNullValue + " - " + json_6.ValueType); var json_7 = STJson.FromObject(12); // made by static function -Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType); +Console.WriteLine("[json_3] - " + json_7.IsNullValue + " - " + json_7.ValueType); /******************************************************************************* * [output] * *******************************************************************************/ -[json_1] - True - None +[json_1] - True - Undefined [json_2] - False - Object [json_3] - False - Array [json_4] - False - Datetime @@ -196,7 +227,7 @@ Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType #CE #HS goldenrod|black -当`ValuteType = Array`时,无法调用`SetItem(+n)`,当`ValueType = Object`时,无法调用`Append(+n)`。但是通过`SetValue(+n)`可强制改变`ValueType`。 +通常情况下`SetItem(+n)`会将`ValueType`设置为`Object`,而`Append(+n)``Insert(+n)`会将`ValueType`设置为`Array`。 #HE 正如上面提到的`STJson`有两个索引器,可以通过索引器访问他们,或者获取值。 @@ -239,17 +270,21 @@ Console.WriteLine(STJson.Serialize(new { key = "this is test" }, 4)); "key": "this is test" } #CE -事实上格式化输出是通过调用静态函数`STJson.Format(+n)`完成的。如果你觉得不喜欢作者内置的格式化风格,完全可以自己写一个格式化的函数。或者去修改源码文件`STJson.Statics.cs:Format(string,int)`。 + +或者你可以直接序列化到一个`TextWriter`中去: +#CS cs +STJson.Serialize(new { key = "this is test" }, new StreamWriter("./test.json"))); +#CE #O string -> object 事实上代码并不会直接将`string`转换为`object`。因为在那之前必须先对字符串进行解析,确保它是一个正确格式的`Json`。但是做完这个过程的时候已经得到一个`STJson`对象了。最后将`STJson`再转换为`object`。 所以你会在源代码`STLib.Json.Converter`中看到如下文件: -`ObjectToSTJson.cs` `ObjectToString.cs` `STJsonToObject.cs` `StringToSTJson.cs` +`ObjectToSTJson.cs` `ObjectToString.cs` `STJsonToObject.cs` 里面并没有`StringToObject.cs`文件,而`STJson.Deserialize(+n)`的源码如下: #CS cs public static T Deserialize(string strJson, +n) { - var json = StringToSTJson.Get(strJson, +n); + var json = STJsonParser.Parse(strJson); return STJsonToObject.Get(json, +n); } #CE @@ -450,6 +485,292 @@ STJson.Deserialize(str); `Attribute`的优先级大于`STJsonSetting`。 #HE +#O JSON5 + +在`STJson(3.0)`中提供了[JSON5](https://json5.org/)的支持,并且作者提供了一些更加便捷的想法,以下列文本为例: +#CS cs,json5.txt +{ + "normal_key": "This is a normal key and value.", // 这是一个行注释。 + /* + 这是一个块注释。 + */ + str_value_1: 'string_1', // 对于键来说["]并不是必须的。、 + str_value_2: "string_2", // 在所有的字符串中你可以选择使用[']或者["]。 + int_numbers: [ // 支持16进制数字 + 0x123, -0x123, +123, -123 + ], + 'float_numbers':[ + .123, 123., -.123, +123., +123E-2 + ], + // 字符串续行 -> https://json5.org + string_1: "string_1.\ +string_2.\ + |<- some space", + + // 但是在STJson中你可以直接使用[\r\n]换行,且保留换行符。 + string_2: "string_1. +string_2. + |<- some space", + + // 甚至这样使用。作者认为JSON5的续行方式不友好。 + // 通过[\]换行后,新行前面不能有空白,不然会被解析到字符串中。 + // 所以STJson允许连续字符串,并最终将其合并为一个字符串。 + string_3: + "string_1." + "string_2." + "\r\nstring_3.", + array:[ + 123,true,"string_1.""string_2.","string_3", + ], +} +#CE + +#CS cs +var str_file = "./json5.txt"; +var str_json = File.ReadAllText(str_file, Encoding.UTF8); +var json = STJson.Deserialize(str_json); +Console.WriteLine(json.ToString(4)); +/******************************************************************************* + * [output] * + *******************************************************************************/ +{ + "normal_key": "This is a normal key and value.", + "str_value_1": "string_1", + "str_value_2": "string_2", + "int_numbers": [ + 291, + -291, + 123, + -123 + ], + "float_numbers": [ + 0.123, + 123, + -0.123, + 123, + 1.23 + ], + "string_1": "string_1.string_2. |<- some space", + "string_2": "string_1.\r\nstring_2.\r\n |<- some space", + "string_3": "string_1.string_2.\r\nstring_3.", + "array": [ + 123, + true, + "string_1.string_2.", + "string_3" + ] +} +#CE + + +#O STJsonReader + +`STJsonReader`可从一个`TextReader`中获取字符并动态解析,通常用于解析大文本数据。如:通过`StreamReader`从文件中或数据流中加载一个`Json`数据。 +#CS cs +string str_json = @" +{ + name: 'DebugST', + language: ['C#', 'JS'], + address: { + country: 'China', + province: 'GuangDong', + city: 'ShenZhen' + } +}"; +using (var reader = new STJsonReader(new StringReader(str_json))) { + foreach (var v in reader) { + //Console.WriteLine(STJson.Serialize(v, 4)); + Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]"); + } +} +/******************************************************************************* + * [output] * + *******************************************************************************/ +name: DebugST - [String] +language: [...] - [Array] +language[0]: C# - [String] +language[1]: JS - [String] +address: {...} - [Object] +address.country: China - [String] +address.province: GuangDong - [String] +address.city: ShenZhen - [String] +#CE + +默认情况下`STJsonReader`递归所有的数据。稍微改一下代码: +#CS cs +foreach (var v in STJson.Read(new StringReader(str_json))) { + if (v.Path == "language") { + Console.WriteLine(v.GetSTJson().ToString(4)); + } else { + Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]"); + } +} +/******************************************************************************* + * [output] * + *******************************************************************************/ +name: DebugST - [String] +[ + "C#", + "JS" +] +address: {...} - [Object] +address.country: China - [String] +address.province: GuangDong - [String] +address.city: ShenZhen - [String] +#CE + +`GetSTJson()`将从当前位置开始向后不停获取数据,直到获取到一个完整的`STJson`对象停止。并且如你所见,在之后的迭代中,已经跳过了`language`,因为他被`GetSTJson()`取走了。 + +#HS pink|black +`STJsonReader`性能远低于`STJson.Deserialize(+n)`,且暂时没有优化`STJsonReader`的打算。作者认为`STJsonReader`仅在特殊场景下使用,而特殊场景不考虑性能。 +#HE + +#HS goldenrod|black +并且由于`STJsonReader`采用的是动态解析,而且`STJson`并非采用的状态机进行解析,作者并不打算做严格的`Json`数据格式校验。所以下面的代码同样能正常运行,且与上面的`str_json`等效。 +#HE +#CS cs +string str_json = @" +{ + name 'DebugST' + language: ['C#' 'JS'], + address: { + country: 'China',,,,,,, + province: 'GuangDong',,,, + city:::::::::::: 'ShenZhen'"; +#CE +#CS cs +string str_json = @" +{ + name: 'DebugST', + language: ['C#', 'JS'], + address: { + country: 'China',,,,,,, + province: 'GuangDong',,,, + city:::::::::::: 'ShenZhen' + }}}}}}}}}} +}1234567890"; +#CE + +#HS pink|black +`STJsonReader`在动态解析时,作者为了方便会忽略`:``,`,这样在读取一个`键值对`的时候只需要读取两个`token`即可。且直接将第一个作为`key`,第二个作为`value`。部分源码如下: +#HE + +#CS cs +private STJsonToken GetNextFilteredToken() +{ + foreach (var v in m_token_reader) { + switch (v.Type) { + case STJsonTokenType.KVSplitor: // : + case STJsonTokenType.ItemSplitor: // , + continue; + default: + return v; + } + } + return STJsonToken.None; +} + +private STJsonReaderItem GetNextObjectKV() +{ + var token_key = this.GetNextFilteredToken(); + switch (token_key.Type) { + case STJsonTokenType.None: + return null; + case STJsonTokenType.ObjectEnd: + this.PopStack(); + return this.GetNextItem(); + case STJsonTokenType.Symbol: + case STJsonTokenType.String: + break; + default: + throw new STJsonParseException(token_key); + } + m_current_stack.Key = token_key.Value; + var token_val = this.GetNextFilteredToken(); + if (token_val.Type == STJsonTokenType.None) { + return null; + //throw new Exception("error"); + } + var item = new STJsonReaderItem(this, token_val) + { + ParentType = STJsonValueType.Object, + Key = token_key.Value, + Text = token_val.Value + }; + return this.CheckValueToken(item, token_val); +} + +private STJsonReaderItem GetNextArrayItem(){...} +#CE + +#O STJsonWriter + +`STJsonWriter`与`STJsonReader`起到相反的作用,`STJsonWriter`可以用于直接向一个`TextWriter`或数据流实时构造并写入`Json`字符串。 +#CS cs +StringWriter sw = new StringWriter(); +//using (var writer = new STJsonWriter(sw)) { +// writer.StartWithArray((w) => +// { +// // writer == w +// }); +//} +STJson.Write(sw, 4).StartWithArray((w) => +{ + for (int i = 0; i < 1; i++) { + Console.WriteLine("Level:" + w.Level); + w.CreateObject(() => + { + Console.WriteLine("Level:" + w.Level); + w + .SetItem("name", "DebugST") + //.SetItem("language", STJson.CreateArray("C#", "JS")) + //.SetItem("language", new string[] { "C#", "JS" }) + .SetArray("language", () => + { + Console.WriteLine("Level:" + w.Level); + w + .Append("C#") + .Append("JS"); + }) + .SetObject("address", () => + { + Console.WriteLine("Level:" + w.Level); + w + .SetItem("country", "china") + .SetItem("province", "GuangDong") + .SetItem("city", "ShenZhen"); + }); + }); + } +}); +Console.WriteLine(sw.ToString()); +/******************************************************************************* + * [output] * + *******************************************************************************/ +Level:1 +Level:2 +Level:3 +Level:3 +[ + { + "name": "DebugST", + "language": [ + "C#", + "JS" + ], + "address": { + "country": "china", + "province": "GuangDong", + "city": "ShenZhen" + } + } +] +#CE + +#HS skyblue|black +`STJsonWriter`采用层层回调的方式,之所以采用这种方式是为了可以与目标`Json`的数据结构层次关系保持一致。如上所示,`STJsonWriter`内部自身管理着回调函数的调用层级关系。 +#HE + #O json_src 在接下来的教程中我们会使用到一些测试数据,数据如下: @@ -555,7 +876,7 @@ Console.WriteLine(json_temp.Select("'a.b'")); [] ["this is a test"] #CE -在字符串中支持`\\`进行转义:`\\r``\\n``\\t``\\f``\\b``\\a``\\v``\\0``\\x..``\\u...``\\.` +在字符串中支持`\\`进行转义:`\\r``\\n``\\t``\\f``\\b``\\a``\\v``\\0``\\x..``\\u....``\\.` #O 通配符 @@ -926,76 +1247,33 @@ Console.WriteLine(STJsonPath.TestExpression( #O 内置函数 #TS return|signature|note -string|typeof(+n)|获取数据类型。 -string|str(+n)|转换为字符串。 -string|upper(+n)|转换为大写。 -string|lower(+n)|转换为小写。 - -long|len(+n)|获取字符串或者数组长度。 -long|long(+n)|转换为整数。 -double|double(+n)|转换为浮点数。 -long or double|abs(+n)|获取绝对值。 -long|round(+n)|四舍五入。 -long|ceil(+n)|向上取整。 +string|typeof(object)|获取数据类型。 +string|str(object)|转换为字符串。 +string|upper(string)|转换为大写。 +string|lower(string)|转换为小写。 + +long|len(string|array)|获取字符串或者数组长度。 +long|long(string|number)|转换为整数。 +double|double(string|number)|转换为浮点数。 +number|abs(number)|获取绝对值。 +long|round(number)|四舍五入。 +long|ceil(number)|向上取整。 + +number|max(array)|求最大值。 +number|min(array)|求最小值。 +number|avg(array)|求平均值。 +number|sum(array)|求总和。 + +string|trim(string)|裁切字符串两端的指定字符。 +string|trims(string)|裁切字符串开始的指定字符。 +string|trime(string)|裁切字符串末尾的指定字符。 +string[]|split(string)|拆分字符串。 +long or string|time(+n)|获取或格式化时间戳。 #TE `typeof`具有下列返回值: `string` `long` `double` `boolean` `array` `object` `undefined` -以上函数可使用如下签名 -#US -fn(object) -fn(object[]) -fn(object,bool) -fn(object[],bool) -#UE -其中`bool`表示是否期望以数组方式获得结果,如果为`true`则以数组方式输出结果,并且当`object`为数组是,递归其中的数据。 -#CS cs -Console.WriteLine(STJsonPath.TestExpression( - null, - null, - "typeof([1+2+3,'abc'], false)" - ).SelectFirst("return").ToString(4)); -/******************************************************************************* - * [output] * - *******************************************************************************/ -{ - "bool": true, - "value_type": "String", - "value": "array" -} -// ============================================================================= -Console.WriteLine(STJsonPath.TestExpression( - null, - null, - "typeof([1+2+3,'abc'], true)" - ).SelectFirst("return").ToString(4)); -/******************************************************************************* - * [output] * - *******************************************************************************/ -{ - "bool": true, - "value_type": "Array", - "items": [ - "long", "string" - ] -} -#CE -不同的函数第二个参数采用不同的默认值。一下是一些其他函数,读者可以自己通过`TestExpression()`进行效果测试。 - -#TS return|signature|note -long or double|max(+1)|求最大值。 -long or double|min(+1)|求最小值。 -long or double|avg(+1)|求平均值。 -long or double|sum(+1)|求总和。 - -string|trim(+n)|裁切字符串两端的指定字符。 -string|trims(+n)|裁切字符串开始的指定字符。 -string|trime(+n)|裁切字符串末尾的指定字符。 -string_array|split(+1)|拆分字符串。 -long or string|time(+n)|获取或格式化时间戳。 -#TE - #O 内置函数列表 随着版本的迭代更新(如果可能的话),内置函数可能随时发生变化,通过`GetBuildInFunctionList()`可以查看当前版本支持的内置函数信息。 @@ -1431,13 +1709,83 @@ Console.WriteLine(json.ToString(4)); 在`Set`中仅仅支持`列表选择器`和`切片选择器`。不支持其他选择器。 #HE -#O 回调函数 + +#O STJsonCreator + +虽然上面通过`Set`也可以很容易的构造出一个`Json`数据,但是对于多层嵌套的数据结构通过`Set`也能实现,但是代码会变得过于复杂并且无法直观的看到`Json`数据的结构。`STJsonCreator`与`STJsonWriter`功能类似,不过`STJsonCreator`是直接创建一个`STJson`对象,通常用于创建复杂结构的`Json`对象。依然使用`ElasticSearch`数据库为例,模拟一个数据检索。 + +#CS cs +var str_json_post = @" +{ + type: 'student', + names: ['DebugST', 'None'], +}"; // 模拟前端提交的请求数据 +var json_post = STJson.Deserialize(str_json_post); +var json_es_query = STJson.Create((c) => // 构造ES检索语法 +{ + c + .SetItem("from", (json_post.GetValue("page", 1) - 1) * json_post.GetValue("size", 10)) + .SetItem("size", json_post.GetValue("size", 10)) + .Set("query.bool", () => + { + c + .Set("filter", () => + { + c.Set("term.type", json_post["type"]); + }) + // c.Set(str_path, bool, callback) bool -> if true, create this path. + .Set("should", !json_post["names"].IsNullOrNullValue(), () => + { + // c.Append(0, 5, 1, (i)=>{ }) => for(int i = 0; i < 5; i+=1) {...} + // c.Append(IEnumerable, callback); + c.Append(json_post["names"], (item) => // => foreach(var item in json_post) {...} + { + c.Set("term.name", item); + }); + }); + }); +}); +Console.WriteLine(json_es_query.ToString(4)); +/******************************************************************************* + * [output] * + *******************************************************************************/ +{ + "from": 0, + "size": 10, + "query": { + "bool": { + "filter": { + "term": { + "type": "student" + } + }, + "should": [ + { + "term": { + "name": "DebugST" + } + }, + { + "term": { + "name": "None" + } + } + ] + } + } +} +#CE + +可以看到通过`POST`过来的请求很容易的就构造出了`ES`数据库的检索语法,并且`Json`的数据结构保持了一致。并且在`Set`和`Append`中提供了多重重载,可以更加便捷的进行逻辑处理,当然它们仅仅是语法糖。通过`if``for`也能达到同样的目的。 + + +#O STJsonPath 回调函数 通过上面的`Set`我们是否能够将`json_src`中每个人的喜好都添加一个`coding`?。。似乎不太可以。毕竟向`hobby`中添加数据需要`append`而不是`set`。 当然也并不是没有办法。 #CS cs -json_src.Select("*.hobby", (p, j) => { - j.Append("coding"); +json_src.Select("*.hobby", (arg) => { + arg.Json.Append("coding"); }); Console.WriteLine(json_src.ToString(4)); /******************************************************************************* @@ -1457,12 +1805,12 @@ Console.WriteLine(json_src.ToString(4)); #CE 在`Select`中支持两种回调函数,一个需要返回值,另一个不需要返回值,如同上面的。而下面的带返回值。 #CS cs -var json = json_src.Select("*.hobby.*", (p, j) => { +var json = json_src.Select("*.hobby.*", (arg) => { return new STJsonPathCallBackResult() { Selected = true, // 是否将此条 json 添加到结果中 - Json = new STJson() // 需要被添加到结果的 json - .SetItem("path", p) - .SetItem("item", j.Value.ToString().ToUpper()) + arg.Json = new STJson() // 需要被添加到结果的 json + .SetItem("path", arg.Path) + .SetItem("item", arg.Json.Value.ToString().ToUpper()) }; }); Console.WriteLine(json.ToString(4)); @@ -1496,12 +1844,12 @@ Console.WriteLine(json.ToString(4)); #CE 可以通过`RestorePathJson()`还原其结构。 #CS cs -var json = json_src.Select("*.hobby.*", (p, j) => { +var json = json_src.Select("*.hobby.*", (arg) => { return new STJsonPathCallBackResult() { Selected = true, - Json = new STJson() - .SetItem("path", p) - .SetItem("item", j.Value.ToString().ToUpper()) + arg.Json = new STJson() + .SetItem("path", arg.Path) + .SetItem("item", arg.Json.Value.ToString().ToUpper()) }; }); Console.WriteLine(STJsonPath.RestorePathJson(json).ToString(4)); @@ -1534,8 +1882,8 @@ Console.WriteLine(STJsonPath.RestorePathJson(json).ToString(4)); 如果想向所有的用户喜好中添加`coding`。但是又不想影响源数据,那么可以克隆一份数据进行操作。 #CS cs -json_src.Clone().Select("*.hobby", (p, j) => { - j.Append("coding"); +json_src.Clone().Select("*.hobby", (arg) => { + arg.Json.Append("coding"); }); Console.WriteLine(json_src.ToString(4)); /******************************************************************************* @@ -1559,14 +1907,11 @@ Console.WriteLine(json_src.ToString(4)); 在`STJson`中内置了部分扩展函数用于聚合操作,进行一些简单的数据处理。结合`STJsonPath`可以快速且简单的完成一些数据操作。 #O sort -`Sort()`用于对数据进行排序,内部采用`归并排序`方式。此函数有如下形式签名: -#TS return|signature|note -STJson|Sort()|不指定`path`进行排序。默认升序。 -STJson|Sort(bool)|不指定`path`进行排序,指定是否为降序。 -STJson|Sort(params object[])|用于指定`path`或是否降序。 -#TE +`Sort()`用于对数据进行排序,内部采用`归并排序`方式,并且`Sort`有多个重载。但最终版本的`Sort`签名如下,所有的重载,最终调此函数。 +#CS cs +public static STJson Sort(this STJson json, bool is_new_instance, STJsonSortCallback callback); +#CE -不指定`path`进行排序: #CS cs var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } }; var json_objs = STJson.FromObject(arr_obj); @@ -1575,29 +1920,39 @@ Console.WriteLine(json_objs.Sort(true)); /******************************************************************************* * [output] * *******************************************************************************/ -[true,1,2,4,5,6,{"aa":"aa"},null] -[6,5,4,2,true,1,{"aa":"aa"},null] -#CE -你是否会感到疑惑为什么会是这样的结果?。。。但是在那之前为什么你不先疑惑一下为什么要用这样的数组去排序呢? -在`Sort`的内部会对每个元素获取出一个数字并记录元素索引,然后对这个数字进行排序,最后根据索引位置重新组合一个数组达到排序目的。对于如何获取元素的数字规则如下: -#CS cs -switch (item.ValueType) { - case STJsonValueType.Long: - case STJsonValueType.Double: - d_temp = item.GetValue(); - break; - case STJsonValueType.Boolean: // true 被计算成 1 - d_temp = item.GetValue() ? 1 : 0; - break; - case STJsonValueType.Datetime: - d_temp = Convert.ToDouble(item.GetValue()); - break; - case STJsonValueType.String: - d_temp = item.Value == null ? 0 : item.Value.ToString().Length; - break; - default: // 对于其他类型让其始终排在最后面即可 - d_temp = arr_b_desc[j] ? double.MinValue : double.MaxValue; - break; +[null,1,2,4,5,6,true,{"aa":"aa"}] +[{"aa":"aa"},true,6,5,4,2,1,null] +#CE +你是否会感到疑惑为什么会是这样的结果?。。。但是在那之前为什么你不先疑惑一下为什么要用这样的数组去排序呢?如上所示`Sort`需要一个回调函数,`STJson`的扩展提供了两个默认的回调函数,它被如下代码构造: +#CS cs +private static STJsonSortCallback BuildDefaultSortCallback(bool is_desc) +{ + return (a, b) => + { + int n_ret = 0; + if (a.IsNullOrNullValue()) { + n_ret = -1; + } else if (b.IsNullOrNullValue()) { + n_ret = 1; + } else if (a.IsNumber && b.IsNumber) { + n_ret = Convert.ToDouble(a.Value) < Convert.ToDouble(b.Value) ? -1 : 1; + } else if (a.ValueType != b.ValueType) { + n_ret = a.ValueType - b.ValueType; + } else { + switch (a.ValueType) { + case STJsonValueType.Boolean: + n_ret = (bool)b.Value ? -1 : 1; + break; + case STJsonValueType.Datetime: + n_ret = (DateTime)a.Value < (DateTime)b.Value ? -1 : 1; + break; + case STJsonValueType.String: + n_ret = string.Compare(a.Value.ToString(), b.Value.ToString()); + break; + } + } + return is_desc ? -n_ret : n_ret; + }; } #CE 所以如果你没有那么奇怪的数组的话,那么一切都会正常起来。如果实在是想只对里面的数字排序怎么办? @@ -1657,59 +2012,6 @@ Console.WriteLine(json_src.Sort("age").ToString(4)); #CS cs Console.WriteLine(json_src.Sort("age", true).ToString(4)); #CE -还可以同时指定多个字段进行排序: -#CS cs -Console.WriteLine(json_src.Sort("gender", false, "age", true).ToString(4)); -/******************************************************************************* - * [output] * - *******************************************************************************/ -[ - { - "name": "Tony", - "age": 16, - "gender": 0, - "hobby": [ - "game", "dance" - ] - }, { - "name": "Tom", - "age": 16, - "gender": 0, - "hobby": [ - "cooking", "sing" - ] - }, { - "name": "Kun", - "age": 26, - "gender": 1, - "hobby": [ - "sing", "dance", "rap", "basketball" - ] - }, { - "name": "Andy", - "age": 20, - "gender": 1, - "hobby": [ - "draw", "sing" - ] - } -] -#CE -以上为使用`gender`进行升序,然后对于相同值再对`age`进行降序排列。`Sort`函数参数规则如下: -#CS cs -Sort(string strJsonPath, bool isDesc[, string strJsonPath, bool isDesc]*); -// STLib.Json.STJsonExtension.cs:Sort() -public static STJson Sort(this STJson json, params object[] fields) { - var jsonSort = STJson.CreateArray(); - for (int i = 0; i < fields.Length; i += 2) { - var j = new STJson() - .SetItem("path", fields[i] == null ? null : fields[i].ToString()) - .SetItem("desc", i + 1 < fields.Length ? Convert.ToBoolean(fields[i + 1]) : false); - jsonSort.Append(j); - } - return json.Sort(jsonSort); -} -#CE #O group @@ -1773,7 +2075,6 @@ Console.WriteLine(json_src.Group("gender", "age").ToString(4)); "age": [...] } #CE -如果想进行嵌套`Group()`应该怎么办?作者并不打算在`Group()`中实现,因为`STJsonPath`可以完成这个工作。 #O terms @@ -1826,7 +2127,7 @@ json_src.Terms("hobby")["hobby"].Count; #CE 而且有没有一种可能`cardinality`只统计个数。。。但是。。如果想要获取到去重后的字段值都有哪些要怎么办?。。。而刚才说的这些情况`Terms()`似乎都已经完成了。。仅仅是没有做排序。都已经有`Sort()`函数了,再单独排个序怎么了? #CS cs -json_src.Terms("hobby")["hobby"].Sort("count", true); +json_src.Terms("hobby").ForEach((item)=>item.Sort("count", true)); #CE 如果想获得人员最受欢迎的前三个喜好要怎么做? #CS cs @@ -1923,7 +2224,9 @@ Console.WriteLine(json_src.Terms("hobby[0]").ToString(4)); ] } #CE + 好像看上去很奇怪的样子。其实`STJsonPath`有一个`Name`属性。 + #CS cs Console.WriteLine(json_src.Terms(new STJsonPath("test", "hobby[0]")).ToString(4)); /******************************************************************************* @@ -1950,7 +2253,7 @@ Console.WriteLine(json_src.Terms(new STJsonPath("test", "hobby[0]")).ToString(4) #T THE END -非常感谢看到最后,作者写的都快要疯掉了,一半的时候就想摆烂放弃了。咬咬牙终于完成了。如果在使用过程中遇到什么问题请一定联系作者,作者一定在第一时间用小本本记录下来。至于改不改。。。日后再说。。 +非常感谢看到最后,如果在使用过程中遇到什么问题请一定及时联系作者,作者一定在第一时间用小本本记录下来。至于改不改。。。日后再说。。。 #HS black|white #US diff --git a/docs/tutorial_en.html b/docs/tutorial_en.html index 53355cb..02ef06d 100644 --- a/docs/tutorial_en.html +++ b/docs/tutorial_en.html @@ -4,7 +4,6 @@ STJson - @@ -17,112 +16,124 @@
-2023-06-07 -> by:DebugST +2024-08-02 -> by:DebugST
-

STLib.STJson

+

STLib.Json

Introduction

-

STLib.STJsonis aJsonparsing library based on theMITopen source protocol. powered by DebugST.The pure native implementation of the library does not rely on any library, so it is very lightweight, convenient, and powerful. Since it does not rely on pure native construction, it can be easily transplanted to other programming languages. Of course, because the author is lazy, the probability is not very high.

-

When designing STJson, the author did not have any objection. After all, there was no dispute about the data format of Json. When designing STJsonPath, the author hesitated, because the author found many controversial places and could not find a formal RFC document about JsonPath, but only found a draft:

+

STLib.Json is a Json parsing library based on the MIT open source protocol . Developed by DebugST, the library is implemented natively without any library dependencies , so it is very lightweight , convenient and powerful . Because there is no use of any dependencies built natively, it can be easily ported to other programming languages. Of course, due to the author is very lazy, this probability is not very big, just dream about it, do not take it too seriously.

+

So why develop STJson? As we all know, .NET comes with a parsing library that is too user-friendly, and I'm sure you're not used to using it. And it just so happens that the author always needs to use JSON data processing at the moment. Although there are some third-party libraries that can be used, after the author's experience, he thought, forget about doing it himself. There are two main points.

+
    +
  • JSON5 support is not friendly enough
  • +
  • JsonPath support is not friendly enough.
  • +
+

In the design of STJson the author does not have any objection, after all, Json data format does not exist controversial place, of course, in the compatibility of JSON5 the author still add part of their own ideas, anyway, the current JSON5 is not the official document.

+

When designing STJsonPath, the authors hesitated because they found a lot of controversy and were unable to find an official RFC document for JsonPath, only a draft:

JSONPath: Query expressions for JSON

-

Although some doubts in the draft can also be solved, the author still has some doubts. After all, it is not an official document. Before development, the author was also curious about other open source libraries of JsonPath, and how other libraries resolve controversial issues. Unfortunately, the problem still exists. No developer seems willing to refer to this draft. Since there is no formal RFC document for JsonPath, the author also wants to establish his own portal. However, the author will not ignore this draft. The author will try to be compatible with the draft as much as possible, and will also add some of his own ideas.

-

Path question

-

JsonPath is derived from the use of XPath, which is known to be the XML Path Language, a language used to determine the location of a part of an XML document. Obviously JsonPath is used for Json. They are both used in almost the same way, but the differences between the two data formats XML and Json are bound to have their differences.

-

For example, there are no special characters in the element name of XML, but the Key of Json can be any string, if there is Json data:

+

Although some of the doubts in the draft can also be solved, the author still has some doubts, after all, it is not an official document. Before development the author was also curious about other JsonPath open source libraries and how other libraries solved the controversial areas. Unfortunately, the problem persists. No library seems to be willing to be compatible with this draft. Since there is no official RFC documentation for JsonPath, the authors will try to be as compatible as possible with the draft while adding some of their own ideas.

+

The question of Path

+

JsonPath is derived from the use of XPath, which is known as the XML Path Language, a language used to determine the location of a part of an XML document. Obviously JsonPath is used for Json. Both of them are used in almost the same way, but the differences in the XML and Json data formats are bound to have differences.

+

For example, there are no special characters in the element name of XML, but the Key of Json can be any string, if there is a bit of Json data:

{ "aa": { "bb": "cc" } }
-

In JsonPath we can get the value cc by the path aa.bb without any problem, what if the Json data is replaced by the following:

+

In JsonPath we can get the value cc by the path aa.bb and there is no problem, what if the Json data is replaced with the following:

{ "a.a": { "bb": "cc" } }
-

Obviously the value cc is not available through a.a.bb. Maybe normally Json represents an object, and objects don't have such strange properties. But for the data format of Json, its Key can be any character. Even if you pass {OBJ_NAME}.a.a.bb in the browser, you will only get an error. But the way the browser accesses the Json element is not just through . , there is also the indexer {OBJ_NAME}['a.a']['bb'] that can get the correct value.

-

The use of ' or " is allowed in STJsonPath, which is marked as String in the lexical parser (STJsonPathTokenizer.cs), and if this string is in the scope of a non-expression, the parser (STJsonPathParser.cs) will re-tag it as Property as an index to avoid special strings that can't be handled, and strings support \ for escaping. So you can use 'a.a'.bb in STJsonPath to get the correct value. And a similar treatment is indeed mentioned in the draft.

-

Expression question

-

In the draft there is mention of supporting expressions and that there are two types of expressions, () and ? (). They stand for ordinary expressions and filtered expressions respectively. And according to the author's understanding, ordinary expression is used to calculate a value and use this value as part of JsonPath, such as $.books[(@.length - 1)] which can be seen in many cases. It is possible to perform recursion on Json elements when executing JsonPath, where @ is a dynamic variable that indicates the Json element currently being processed during the recursion, and according to the surface meaning of the expression is trying to get the last element in books.

-

But how does @.length get executed? Where does length come from? If @ is an array, the author can tentatively understand that it is finding the length of the array? But what if @ is an object?

-

Since it is an expression, what kind of syntax can be written in ()? What about the syntax? About all these queries the author has used his own implementation, which will be introduced in a later tutorial.

+

Obviously the value cc is not available through a.a.bb. Perhaps normally Json represents some object, and objects don't have such strange properties. But for the Json data format, its Key can be any character. Even if you pass {OBJ_NAME}.a.a.bb in the browser, you'll just get an error. But browsers don't access Json elements only by . , but also the indexer {OBJ_NAME}['a.a']['bb'] to get the correct value.

+

The use of ' or " is allowed in STJsonPath, which will be marked as String in the lexical parser (STJsonPathTokenizer.cs), and if this string is in the scope of a non-expression, the parser (STJsonPathParser.cs) will re-tag it as Property to be used as an index, thus avoiding cases where special strings can't be handled, and strings support \ for escaping. So you can use 'a.a'.bb in STJsonPath to get the correct value. And similar handling is indeed mentioned in the draft.

+

The question of expression

+

In the draft there is a reference to support for expressions and that there are two types of expressions, () and ? (). These represent regular expressions and filter expressions respectively. According to the author's understanding, regular expression is used to calculate a value and use this value as part of JsonPath, such as $.books[(@.length - 1)] in many cases. When executing JsonPath it is possible to perform a cascading recursion on the Json elements, where @ is a dynamic variable indicating the Json element currently being processed during the recursion, and according to the expression's apparent meaning it is trying to get the penultimate element in books.

+

But how does @.length get executed? Where does length come from? If @ is an array, the author can tentatively understand that he is asking for the length of the array? But what if @ is an object?

+

Since it is an expression, what kind of syntax can be written in ()? What about the grammar? Regarding all these queries the author has used his own implementation, which will be presented in a later tutorial.

STJson API

-

Data mapping

+

Data type

@@ -142,69 +153,74 @@

STJson API

.NetSTJsonValueType.NetSTJsonValueType
IDectionaryObjectobjectObject
-

STJson covers the common basic data types, even if they are not included, then the properties of the object will be recurred by performing reflection in the end.

-

Static functions

+

STJson covers common basic data types, and even if they are not included, then the properties of the object will eventually be recursed by performing reflection.

+

Static methods

-

(+n) in the following indicates that there are multiple overloads.

+

In the following, (+n) means that there is more than one overloading.

- - + + - - - - - - + + + + + + + + +
returnsignaturenote
stringSerialize(+n)Serialize an object object to a string.
STJsonDeserialize(string)Convert a string to STJson object order.
stringSerialize(+n)Serializes an object object to a string.
STJsonDeserialize(string)Convert a string to an STJson object.
TDeserialize<T>(+n)Converts an object or string to a target object.
STJsonSTJson CreateObject()Create an empty object。
STJsonSTJson CreateArray(params object[])Create an array object。
STJsonFromObject(+n)Converts an object to STJson.
stringFormat(+n)Format a Json string.
voidAddCustomConverter(+n)Add custom type converter.
voidRemoveCustomConverter(+n)Remove custom type converter
STJsonWriterWriteCreate an STJsonWriter object.
STJsonReaderReadCreate an STJsonReader object.
STJsonCreate(STJsonCreator)Create an STJson object.
STJsonCreateObject()Creates an Null object.
STJsonCreateArray(params object[])Creates an array object.。
STJsonFromObject(+n)Convert an object to STJson.
stringFormat(+n)Formats a Json string.
voidAddCustomConverter(+n)Custom type converters.
voidRemoveCustomConverter(+n)Remove the custom converter.
-

Non-Static functions

+

Non-Static methods

- - - - - - - - - - + + + + + + + + + + +
returnsignaturenote
STJsonSetItem(+n)Adds a key-value pair to the object and returns itself.
STJsonSetKey(string)Adds a key to the object and returns the target object.
voidSetValue(+n)Set the value of the target object.
STJsonDelete(string)Removes a key from the object and returns the target object.
STJsonAppend(+n)Adds one or some elements to the array object and returns itself.
STJsonInsert(+n)Inserts an element into the array object and returns itself.
STJsonRemoveAt(int nIndex)Removes an index from the array object.
voidClear()Clear all child elements.
IEnumerator<STJson>GetEnumerator()Get all the child elements of the current element.
STJsonCloneClone the current element.
STJsonSetItem(+n)Add a key-value pair to the object and returns itself.
STJsonSetKey(string)Add a key to the object and returns the target object.
voidGetValue(+n)Get the value of the target object.
voidSetValue(+n)Set the value of the target object.
STJsonRemove(string)Remove a key from an object and returns the target object.
STJsonAppend(+n)Add an element or elements to an array object and returns itself.
STJsonInsert(+n)Insert an element or elements to an array object and returns itself.
STJsonRemoveAt(int nIndex)Remove an index from an array object.
voidClear()Clear all elements.
IEnumerator<STJson>GetEnumerator()Get all elements.
STJsonCloneClone current STJson
-

Extended functions

+

Extensions methods

- - - - - - - - - - - - - + + + + + + + + + + + + + +
returnsignaturenote
STJsonSet(+n)Set the object according to the path (STJsonPath).
stringGetValue(+n)Get the string value of the object (value-type only)
TGetValue<T>(+n)Get the value of the object (value-type only)
STJsonSelect(+n)Filter data in an object.
STJsonSelectFirst(+n)Filter data in the object and select the first result.
STJsonSelectLast(+n)Filters through the data in the object and selects the last result.
STJsonGroup(+n)Groups the specified fields.
STJsonTerms(+n)Count the number of values for the specified field.
STJsonSort(+n)Sort the specified fields.
STJsonMin(+n)Tally the minimum value of the specified field.
STJsonMax(+n)Tally the maximum value of the specified field.
STJsonSum(+n)Tally the total number of the specified fields.
STJsonAvg(+n)Tally the average of the specified fields.
boolIsNullOrNullValueCheck current STJson is null or the Undefined.
STJsonSet(+n)Set the object according to the path (STJsonPath).
stringGetValue(+n)Get the string value of the object (value type only).
TGetValue<T>(+n)Get value from value(value type only).
STJsonSelect(+n)Filter the data in the object.
STJsonSelectFirst(+n)Filter the data in the object and check the first result.
STJsonSelectLast(+n)Filter the data in the object and check the last result.
STJsonGroup(+n)Groups the specified fields.
STJsonTerms(+n)Counts the number of values in the specified field.
STJsonSort(+n)Sort the array.
STJsonMin(+n)Get the minimum value from the array.
STJsonMax(+n)Get the maximum value from the array.
STJsonSum(+n)Get the summary from the array.
STJsonAvg(+n)Get the average value from the array.

Fields

- - - + + + - +
typenamenote
stringKeythe Key of the parent element of the current `STJson
objectValuethe value of the current STJson, or null if the current STJson is not of value type
boolIsNullObjectwhether the current STJson is a null element, i.e. the data type of the current STJson value cannot be determined.
stringKeyThe Key of the current STJson parent element.
objectValueThe value of the current STJson, or null if the current STJson is not a value type.
boolIsNullValueWhether the current STJson is an empty element, i.e. it is not possible to determine the data type of the current STJson value.
intCountThe number of child elements contained in the current STJson.
STJsonValueTypeValueType(enumeration) The data type of the current element.
STJsonValueTypeValueType(Enumeration) The data type of the current element.

STJsonValueType is the following value:

@@ -214,11 +230,20 @@

STJson API

  • STJson STJson[int]
  • STJson STJson[string]
  • -

    STJson [Basic]

    -

    STJson is an intermediate data type that is a bridge between string and object and is very convenient to use, e.g:

    +

    Other object

    +
    + + + + + +
    namenote
    STJsonCreatorUsed to create a complex STJson object.
    STJsonReaderUsed to dynamically parse objects from a TextReader. Typically used to read a Json with larger data from a file.
    STJsonWriterUsed to write a Json string to a TextWriter. Typically used to write a large and complex Json data directly to a file.
    +
    +

    STJson [Basic]

    +

    STJson is an intermediate data type that bridges between string and object and is very convenient to use, for example:

    var st_json = new STJson()
    -    .SetItem("number", 0)               // The function returns itself, so it can be operated continuously
    +    .SetItem("number", 0)               // The function returns itself, so it can operate continuously.
         .SetItem("boolean", true)
         .SetItem("string", "this is string")
         .SetItem("datetime", DateTime.Now)
    @@ -250,37 +275,37 @@ 

    STJson [Basic]

    }
    -

    When executing var st_json = new STJson(), st_json is the empty element, i.e. st_json.IsNullObject = true. Because at this point it is not possible to determine whether st_json is an object or an array or a value.

    -

    There are no objects like JArray or JObject in STJson, STJson can be either Array or Object. STJson has two indexers [int][string].

    +

    When executing var st_json = new STJson(), st_json is the empty element, i.e. st_json.IsNullValue = true. Because at this point it is not possible to determine whether st_json is an object or an array or a value.

    +

    There is no object like JArray or JObject in STJson, STJson can be either Array or Object. STJson has two indexers [int] [string].

    var json_1 = new STJson();
    -Console.WriteLine("[json_1] - " + json_1.IsNullObject + " - " + json_1.ValueType);
    +Console.WriteLine("[json_1] - " + json_1.IsNullValue + " - " + json_1.ValueType);
     
     var json_2 = new STJson();
     json_2.SetItem("key", "value");
    -Console.WriteLine("[json_2] - " + json_2.IsNullObject + " - " + json_2.ValueType);
    +Console.WriteLine("[json_2] - " + json_2.IsNullValue + " - " + json_2.ValueType);
     
     var json_3 = new STJson();
     json_3.Append(1, 2, 3);
    -Console.WriteLine("[json_3] - " + json_3.IsNullObject + " - " + json_3.ValueType);
    +Console.WriteLine("[json_3] - " + json_3.IsNullValue + " - " + json_3.ValueType);
     
     var json_4 = new STJson();
     json_4.SetValue(DateTime.Now);
    -Console.WriteLine("[json_4] - " + json_4.IsNullObject + " - " + json_4.ValueType);
    +Console.WriteLine("[json_4] - " + json_4.IsNullValue + " - " + json_4.ValueType);
     
     var json_5 = STJson.CreateArray();          // made by static function
    -Console.WriteLine("[json_5] - " + json_5.IsNullObject + " - " + json_5.ValueType);
    +Console.WriteLine("[json_5] - " + json_5.IsNullValue + " - " + json_5.ValueType);
     
     var json_6 = STJson.CreateObject();         // made by static function
    -Console.WriteLine("[json_6] - " + json_6.IsNullObject + " - " + json_6.ValueType);
    +Console.WriteLine("[json_6] - " + json_6.IsNullValue + " - " + json_6.ValueType);
     
     var json_7 = STJson.FromObject(12);         // made by static function
    -Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType);
    +Console.WriteLine("[json_3] - " + json_7.IsNullValue + " - " + json_7.ValueType);
     /*******************************************************************************
      *                                [output]                                     *
      *******************************************************************************/
    -[json_1] - True - None
    +[json_1] - True - Undefined
     [json_2] - False - Object
     [json_3] - False - Array
     [json_4] - False - Datetime
    @@ -289,9 +314,9 @@ 

    STJson [Basic]

    [json_7] - False - Long
    -

    When ValuteType = Array, SetItem(+n) cannot be called, and when ValueType = Object, Append(+n) cannot be called. But ValueType can be forced to change by SetValue(+n).

    +

    Normally SetItem(+n) sets the ValueType to Object, and Append(+n) Insert(+n) sets the ValueType to Array.

    -

    As mentioned above STJson has two indexers through which you can access them, or get the values.

    +

    As mentioned above STJson has two indexers that can be used to access them, or get values.

    var json_temp = STJson.CreateArray()
         .SetItem("string", "this is string")
    @@ -308,9 +333,9 @@ 

    STJson [Basic]

    "2" 2
    -

    object -> string

    -

    By the above example maybe you already know how to convert an object to string, by STJson.FromObject(object).ToString(+n), but is it possible that it actually doesn't need to be so troublesome? For example: STJson.Serialize(+n) will do?

    -

    In fact STJson.Serialize(+n) would be more efficient because it converts the object directly to a string, rather than converting it to STJson and then to a string.

    +

    object -> string

    +

    With the above example maybe you already know how to convert an object to string, by STJson.FromObject(object).ToString(+n), but is it possible that it doesn't really have to be so tedious? For example, STJson.Serialize(+n) would be fine????

    +

    Actually STJson.Serialize(+n) would be more efficient, because it converts the object directly to a string, instead of converting to STJson and then to a string.

    Console.WriteLine(STJson.Serialize(new { key = "this is test" }));
     /*******************************************************************************
    @@ -328,20 +353,43 @@ 

    STJson [Basic]

    "key": "this is test" }
    -

    In fact formatting the output is done by calling the static function STJson.Format(+n). If you don't like the author's built-in formatting style, you can write a formatting function of your own. Or go modify the source file STJson.Statics.cs:Format(string,int).

    -

    string -> object

    -

    The code doesn't actually convert string to object directly. It has to parse the string to make sure it's a properly formatted Json before it does that. But by the time this is done, you'll already have an STJson object. Finally, STJson is converted to object again.

    +

    Or you can serialize directly into a TextWriter:

    +
    +
    STJson.Serialize(new { key = "this is test" }, new StreamWriter("./test.json")));
    +
    +

    string -> object

    +

    In fact the code does not directly convert string to object. That's because the string has to be parsed before that to make sure it's a properly formatted Json. But by the time it's done, it's already got an STJson object. Finally the STJson is converted to object.

    So you will see the following file in the source code STLib.Json.Converter:

    -

    ObjectToSTJson.cs ObjectToString.cs STJsonToObject.cs StringToSTJson.cs

    -

    There is no StringToObject.cs file inside, and the source code of STJson.Deserialize(+n) is as follows:

    +

    ObjectToSTJson.cs ObjectToString.cs STJsonToObject.cs

    +

    There is no StringToObject.cs file in there, and the source code for STJson.Deserialize(+n) is as follows:

    public static T Deserialize<T>(string strJson, +n) {
    -    var json = StringToSTJson.Get(strJson, +n);
    +    var json = STJsonParser.Parse(strJson);
         return STJsonToObject.Get<T>(json, +n);
     }
    -

    STJsonConverter

    -

    Although there are a lot of data type conversions built into STJson, even data types that are not available are treated as object for recursive processing. But sometimes the situation is not very friendly. For example:

    +

    STJson -> object

    +

    I'm sure the author doesn't need to explain how to convert a string to an object and the reader should know how to handle it, but it's worth stating here that STJson can be appended to an object to enable localized updates.

    +
    +
    public class TestClass {
    +    public int X;
    +    public int Y;
    +}
    +
    +TestClass tc = new TestClass() {
    +    X = 10,
    +    Y = 20
    +};
    +STJson json_test = new STJson().SetItem("Y", 100);
    +STJson.Deserialize(json_test, tc);
    +Console.WriteLine(STJson.Serialize(tc));
    +/*******************************************************************************
    + *                                [output]                                     *
    + *******************************************************************************/
    + {"X":10,"Y":100}
    +
    +

    STJsonConverter

    +

    Although there are a lot of type conversions built into STJson, and even types that aren't there are recursive. But sometimes the situation is not very friendly. For example:

    Rectangle rect = new Rectangle(10, 10, 100, 100);
     Console.WriteLine(STJson.Serialize(rect, 4));
    @@ -370,7 +418,7 @@ 

    STJson [Basic]

    "IsEmpty": false }
    -

    Obviously, this result is too complicated because all the fields of Rectangle are recursive. But what if this is the case?

    +

    Obviously, this result is overly complicated because all the fields of Rectangle are recursively out. But what if this is the case?

    public class RectangleConverter : STJsonConverter
     {
    @@ -414,9 +462,9 @@ 

    STJson [Basic]

    {X=10,Y=10,Width=200,Height=200}
    -

    where bProcessed is passed in as true by default, and the default processing is used when the upper-level function gets false.

    +

    Where bProcessed is passed in with a default value of true, and the default processing is used when the higher level function gets false.

    -

    The STJsonConverter provides the Attribute class, which can also be used to tag object attributes.

    +

    STJsonConverter provides the Attribute class, which can also be used to mark object attributes.

    public class Test{
         [STJsonConverter(typeof(RectangleConverter))]
    @@ -441,8 +489,8 @@ 

    STJson [Basic]

    } }
    -

    STJsonAttribute

    -

    Maybe you don't want to output all the attributes when serializing, then you can control it with STJsonAttribute.

    +

    STJsonAttribute

    +

    Maybe you don't want to output all the attributes when serializing, then you can control that with STJsonAttribute.

    [STJson(STJsonSerilizaMode.Include)]    // optional
     public class Student
    @@ -474,8 +522,8 @@ 

    STJson [Basic]

    *******************************************************************************/
    {"test_name":"Tom","Hobby":["Cooking","Sports"]}
    -

    STJsonSetting

    -

    STJsonSetting is used to add some personalized settings to the serialization or deserialization. Originally the settings were global. But with the increase of some setting items, the author thinks that the global setting is too sticky, so the independent STJsonSetting is used for decoupling. At the same time the independent setting class can also facilitate the subsequent version of the feature expansion, of course the probability of subsequent versions is not very large. Unless the author does not want to be a salty fish.

    +

    STJsonSetting

    +

    STJsonSetting is used to add some personalized settings in serialization or deserialization. Originally, the settings were global. But with the increase of some settings, the author thinks the global settings are too coupled, so STJsonSetting is independent to decouple them. And the independent settings class can also facilitate the expansion of functionality in subsequent versions, of course, the probability of subsequent versions is not very large. Because the author is very lazy.

    var stu = new Student() {
         Name = "Tom",
    @@ -503,10 +551,278 @@ 

    STJson [Basic]

    }
    -

    The priority of Attribute is greater than that of STJsonSetting.

    +

    Attribute has a higher priority than STJsonSetting.

    +
    +

    JSON5

    +

    Support for JSON5 is provided in STJson(3.0), and the author provides some more convenient ideas, as exemplified by the following text:

    +json5.txt +
    +
    {
    +    "normal_key": "This is a normal key and value.", // this is a line comment。
    +    /*
    +       this is a block comment。
    +    */
    +    str_value_1: 'string_1',        // For a key the ["] is not must.
    +    str_value_2: "string_2",        // For a string you can use ['] or ["]
    +    int_numbers: [                  // Hex number
    +        0x123, -0x123, +123, -123
    +    ],
    +    'float_numbers':[
    +        .123, 123., -.123, +123., +123E-2
    +    ],
    +    // From json 5 -> https://json5.org
    +    string_1: "string_1.\
    string_2.\
    |<- some space"
    , + + // But in STJson you can just use [\r\n] line breaks and keep the line breaks. + string_2: "string_1.
    string_2.
    |<- some space"
    , + + // Or even use it that way. The authors consider JSON5's line continuation to be unfriendly. + // After a line break via [\], the new line cannot be preceded by whitespace or it will be parsed into the string. + // So STJson allows consecutive strings and eventually merges them into a single string. + string_3: + "string_1." + "string_2." + "\r\nstring_3.", + array:[ + 123,true,"string_1.""string_2.","string_3", + ], +}
    +
    +
    +
    var str_file = "./json5.txt";
    +var str_json = File.ReadAllText(str_file, Encoding.UTF8);
    +var json = STJson.Deserialize(str_json);
    +Console.WriteLine(json.ToString(4));
    +/*******************************************************************************
    + *                                [output]                                     *
    + *******************************************************************************/
    +{
    +    "normal_key": "This is a normal key and value.",
    +    "str_value_1": "string_1",
    +    "str_value_2": "string_2",
    +    "int_numbers": [
    +        291,
    +        -291,
    +        123,
    +        -123
    +    ],
    +    "float_numbers": [
    +        0.123,
    +        123,
    +        -0.123,
    +        123,
    +        1.23
    +    ],
    +    "string_1": "string_1.string_2.    |<- some space",
    +    "string_2": "string_1.\r\nstring_2.\r\n        |<- some space",
    +    "string_3": "string_1.string_2.\r\nstring_3.",
    +    "array": [
    +        123,
    +        true,
    +        "string_1.string_2.",
    +        "string_3"
    +    ]
    +}
    +
    +

    STJsonReader

    +

    STJsonReader can take characters from a TextReader and parse them dynamically, usually used for parsing large text data. E.g. load a Json data from a file or stream via StreamReader.

    +
    +
    string str_json = @"
    +{
    +    name: 'DebugST',
    +    language: ['C#', 'JS'],
    +    address: {
    +        country: 'China',
    +        province: 'GuangDong',
    +        city: 'ShenZhen'
    +    }
    +}";
    +using (var reader = new STJsonReader(new StringReader(str_json))) {
    +    foreach (var v in reader) {
    +        //Console.WriteLine(STJson.Serialize(v, 4));
    +        Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]");
    +    }
    +}
    +/*******************************************************************************
    + *                                [output]                                     *
    + *******************************************************************************/
    +name: DebugST - [String]
    +language: [...] - [Array]
    +language[0]: C# - [String]
    +language[1]: JS - [String]
    +address: {...} - [Object]
    +address.country: China - [String]
    +address.province: GuangDong - [String]
    +address.city: ShenZhen - [String]
    +
    +

    By default STJsonReader recurses all data. Change the code slightly:

    +
    +
    foreach (var v in STJson.Read(new StringReader(str_json))) {
    +    if (v.Path == "language") {
    +        Console.WriteLine(v.GetSTJson().ToString(4));
    +    } else {
    +        Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]");
    +    }
    +}
    +/*******************************************************************************
    + *                                [output]                                     *
    + *******************************************************************************/
    +name: DebugST - [String]
    +[
    +    "C#",
    +    "JS"
    +]
    +address: {...} - [Object]
    +address.country: China - [String]
    +address.province: GuangDong - [String]
    +address.city: ShenZhen - [String]
    +
    +

    GetSTJson() will keep fetching data backwards from the current position until it stops fetching a complete STJson object. And as you can see, in subsequent iterations, language has been skipped because he was fetched by GetSTJson().

    +
    +

    STJsonReader performance is much lower than STJson.Deserialize(+n), and there is no intention to optimize STJsonReader at this time. The authors believe that STJsonReader is only used in special scenarios, however special scenarios are not considered for performance.

    +
    +
    +

    And since STJsonReader uses dynamic parsing, and STJson is not parsed by a state machine, the author does not intend to do strict Json data format checking. So the following code also works fine and is equivalent to str_json above.

    +
    +
    +
    string str_json = @"
    +{
    +    name 'DebugST'
    +    language: ['C#' 'JS'],
    +    address: {
    +        country: 'China',,,,,,,
    +        province: 'GuangDong',,,,
    +        city:::::::::::: 'ShenZhen'";
    +
    +
    +
    string str_json = @"
    +{
    +    name: 'DebugST',
    +    language: ['C#', 'JS'],
    +    address: {
    +        country: 'China',,,,,,,
    +        province: 'GuangDong',,,,
    +        city:::::::::::: 'ShenZhen'
    +    }}}}}}}}}}
    +}1234567890";
    +
    +
    +

    When STJsonReader is dynamically parsed, the author ignores :,. This way, when reading a key-value pair, you only need to read two tokens. The first one is used as the key and the second one is used as the value. Part of the source code is as follows:

    -

    json_src

    -

    In the next tutorials we will use some test data, which are as follows:

    +
    +
    private STJsonToken GetNextFilteredToken()
    +{
    +    foreach (var v in m_token_reader) {
    +        switch (v.Type) {
    +            case STJsonTokenType.KVSplitor:     // :
    +            case STJsonTokenType.ItemSplitor:   // ,
    +                continue;
    +            default:
    +                return v;
    +        }
    +    }
    +    return STJsonToken.None;
    +}
    +
    +private STJsonReaderItem GetNextObjectKV()
    +{
    +    var token_key = this.GetNextFilteredToken();
    +    switch (token_key.Type) {
    +        case STJsonTokenType.None:
    +            return null;
    +        case STJsonTokenType.ObjectEnd:
    +            this.PopStack();
    +            return this.GetNextItem();
    +        case STJsonTokenType.Symbol:
    +        case STJsonTokenType.String:
    +            break;
    +        default:
    +            throw new STJsonParseException(token_key);
    +    }
    +    m_current_stack.Key = token_key.Value;
    +    var token_val = this.GetNextFilteredToken();
    +    if (token_val.Type == STJsonTokenType.None) {
    +        return null;
    +        //throw new Exception("error");
    +    }
    +    var item = new STJsonReaderItem(this, token_val)
    +    {
    +        ParentType = STJsonValueType.Object,
    +        Key = token_key.Value,
    +        Text = token_val.Value
    +    };
    +    return this.CheckValueToken(item, token_val);
    +}
    +
    +private STJsonReaderItem GetNextArrayItem(){...}
    +
    +

    STJsonWriter

    +

    STJsonWriter plays the opposite role to STJsonReader, STJsonWriter can be used to construct and write Json strings in real time directly to a TextWriter or stream.

    +
    +
    StringWriter sw = new StringWriter();
    +//using (var writer = new STJsonWriter(sw)) {
    +//    writer.StartWithArray((w) =>
    +//    {
    +//        // writer == w
    +//    });
    +//}
    +STJson.Write(sw, 4).StartWithArray((w) =>
    +{
    +    for (int i = 0; i < 1; i++) {
    +        Console.WriteLine("Level:" + w.Level);
    +        w.CreateObject(() =>
    +        {
    +            Console.WriteLine("Level:" + w.Level);
    +            w
    +            .SetItem("name", "DebugST")
    +            //.SetItem("language", STJson.CreateArray("C#", "JS"))
    +            //.SetItem("language", new string[] { "C#", "JS" })
    +            .SetArray("language", () =>
    +            {
    +                Console.WriteLine("Level:" + w.Level);
    +                w
    +                .Append("C#")
    +                .Append("JS");
    +            })
    +            .SetObject("address", () =>
    +            {
    +                Console.WriteLine("Level:" + w.Level);
    +                w
    +                .SetItem("country", "china")
    +                .SetItem("province", "GuangDong")
    +                .SetItem("city", "ShenZhen");
    +            });
    +        });
    +    }
    +});
    +Console.WriteLine(sw.ToString());
    +/*******************************************************************************
    + *                                [output]                                     *
    + *******************************************************************************/
    +Level:1
    +Level:2
    +Level:3
    +Level:3
    +[
    +    {
    +        "name": "DebugST",
    +        "language": [
    +            "C#",
    +            "JS"
    +        ],
    +        "address": {
    +            "country": "china",
    +            "province": "GuangDong",
    +            "city": "ShenZhen"
    +        }
    +    }
    +]
    +
    +
    +

    STJsonWriter uses a hierarchical approach to callbacks in order to be consistent with the data structure hierarchy of the target Json. As shown above, STJsonWriter manages the level of callback functions internally.

    +
    +

    json_src [Important]

    +

    In the next tutorial we will use some test data, which is as follows:

    test.json
    [{
    @@ -531,16 +847,16 @@ 

    STJson [Basic]

    ] }]
    -

    To load it into the program:

    +

    Load it into the program:

    var json_src = STJson.Deserialize(System.IO.File.ReadAllText("./test.json"));
    -

    The appearance of json_src in the subsequent cases is the above object.

    +

    Subsequent cases where json_src appears are the above objects.

    -

    STJsonPath

    -

    In the source code STJsonExtension.cs the functionality of STJson is extended and some of the functionality of STJsonPath is integrated in it. So there is no dependency on STJsonPath in the original code of STJson, and STJson can be used independently. But STJsonPath, as a helper class of STJson, needs to depend on STJson.

    -

    Selector

    +

    STJsonPath

    +

    In the source code STJsonExtension.cs, the functionality of STJson is extended, which integrates some functionality of STJsonPath. So there is no dependency on STJsonPath in the original STJson code, STJson can be used independently. But STJsonPath is a helper class of STJson, it needs to depend on STJson.

    +

    Selector

    @@ -556,7 +872,7 @@

    STJsonPath

    tokennote
    [? (<expression>)]expression selector, used to enter an arithmetic expression and convert the result to a boolean value to decide whether to continue the selection.
    -

    How to use

    +

    How to use

    An STJsonPath can be constructed by:

    // var jp = new STJsonPath("$[0]name");
    @@ -603,8 +919,8 @@ 

    STJsonPath

    [] ["this is a test"]
    -

    Support for \ in strings for escaping:\r\n\t\f\b\a\v\0\x..\u...\.

    -

    Wildcard character

    +

    Support for \ in strings for escaping:\r\n\t\f\b\a\v\0\x..\u....\.

    +

    Wildcard character

    Wildcards can represent any node of the current level. Gets the names of all people.

    Console.WriteLine(json_src.Select("*.name").ToString(4));
    @@ -615,7 +931,7 @@ 

    STJsonPath

    "Tom", "Tony", "Andy", "Kun" ]
    -

    Depth selector

    +

    Depth selector

    Depth selectors are similar to wildcards, but depth selectors can be at any level.

    Console.WriteLine(json_src.Select("..name").ToString(4));
    @@ -626,7 +942,7 @@ 

    STJsonPath

    "Tom", "Tony", "Andy", "Kun" ]
    -

    List selector

    +

    List selector

    List selectors support both int and string. Although two list selectors are listed in the table of selectors above, there is only one list selector in STJsonPath, and they can be mixed, for example, the following uses are both legal:

    • [0, 1, 2, 3]
    • @@ -697,7 +1013,7 @@

      STJsonPath

      } ]
    -

    Slice selector

    +

    Slice selector

    The slice selector is used to select a fragment in an array. The default value of the slice selector is [0:-1:1], which is implemented inside the slice selector as follows:

    case STJsonPathItem.ItemType.Slice:
    @@ -750,7 +1066,7 @@ 

    STJsonPath

    } ]
    -

    Expression

    +

    Expression

    The following operators can be supported in [?()], with increasing priority from top to bottom.


      @@ -776,7 +1092,7 @@

      STJsonPath

    • [[? (<expression>) - Filter expression that computes a boolean value and determines whether to continue the match.
    • [(<expression>) - A general expression that computes a value and continues the match with the value as an index.
    -

    Filter expression

    +

    Filter expression

    Check the elements of name that contain the letters ku:

    //Console.WriteLine(json_src.Select("*.[?(@.name == 'kun')]").ToString(4));
    @@ -813,7 +1129,7 @@ 

    STJsonPath

    } ]
    -

    General expression

    +

    General expression

    The general expression will continue matching the result as part of STJsonPath.

    Console.WriteLine(json_src.Select("*.[('na' + 'me')]").ToString(4));
    @@ -846,7 +1162,7 @@ 

    STJsonPath

    ]

    You can see that 'name' and 'age' are not valid for hobby because hobby is an array.

    -

    Test expression

    +

    Test expression

    The author provides a static test function TestExpression() that can be used to debug the expression. If there is something you don't understand, test it and you will see the process and the result.

    Console.WriteLine(STJsonPath.TestExpression(
    @@ -945,79 +1261,34 @@ 

    STJsonPath

    } }
    -

    Build-In functions

    +

    Build-In functions

    - - - - - - - - - - -
    returnsignaturenote
    stringtypeof(+n)Get the data type.
    stringstr(+n)Convert to string.
    stringupper(+n)Convert to uppercase.
    stringlower(+n)Convert to lowercase.
    longlen(+n)Get the length of a string or an array.
    longlong(+n)Convert to integer.
    doubledouble(+n)Convert to double.
    long or doubleabs(+n)Get the absolute value.
    longround(+n)Round up to round down.
    longceil(+n)Round up.
    -
    -

    typeof has the following return values:

    -

    string long double boolean array object undefined

    -

    The above functions can be signed with the following signature:

    -
      -
    • fn(object)
    • -
    • fn(object[])
    • -
    • fn(object,bool)
    • -
    • fn(object[],bool)
    • -
    -

    where bool indicates whether the result is expected as an array, if true then the result is output as an array, and when object is an array, the data in it is recursive.

    -
    -
    Console.WriteLine(STJsonPath.TestExpression(
    -    null,
    -    null,
    -    "typeof([1+2+3,'abc'], false)"
    -    ).SelectFirst("return").ToString(4));
    -/*******************************************************************************
    - *                                [output]                                     *
    - *******************************************************************************/
    -{
    -    "bool": true,
    -    "value_type": "String",
    -    "value": "array"
    -}
    -// =============================================================================
    -Console.WriteLine(STJsonPath.TestExpression(
    -    null,
    -    null,
    -    "typeof([1+2+3,'abc'], true)"
    -    ).SelectFirst("return").ToString(4));
    -/*******************************************************************************
    - *                                [output]                                     *
    - *******************************************************************************/
    -{
    -    "bool": true,
    -    "value_type": "Array",
    -    "items": [
    -        "long", "string"
    -    ]
    -}
    -
    -

    Different functions use different default values for the second parameter. The following are some other functions that the reader can test the effect themselves by TestExpression().

    -
    - - - - - - - - - + + + + + + + + + + + + + + + + +
    returnsignaturenote
    long or doublemax(+1)Get the maximum value.
    long or doublemin(+1)Get the minimum value.
    long or doubleavg(+1)Get the average.
    long or doublesum(+1)Get the summary.
    stringtrim(+n)Crop the specified characters at both ends of the string.
    stringtrims(+n)Cuts the character at the beginning of the string.
    stringtrime(+n)Cuts the character at the end of the string.
    stringtypeof(object)Get the data type.
    stringstr(object)Convert to string.
    stringupper(string)Convert to uppercase.
    stringlower(string)Convert to lowercase.
    longlen(string or array)Get the length of a string or an array.
    longlong(string or number)Convert to integer.
    doubledouble(string or number)Convert to double.
    numberabs(number)Get the absolute value.
    longround(number)Round up to round down.
    longceil(number)Round up.
    long or doublemax(array)Get the maximum value.
    long or doublemin(array)Get the minimum value.
    long or doubleavg(array)Get the average.
    long or doublesum(array)Get the summary.
    stringtrim(string)Crop the specified characters at both ends of the string.
    stringtrims(string)Cuts the character at the beginning of the string.
    stringtrime(string)Cuts the character at the end of the string.
    string_arraysplit(+1)split the string.
    long or stringtime(+n)Get or format the timestamp.
    -

    Build-In functions list

    +

    typeof has the following return values:

    +

    string long double boolean array object undefined

    +

    Build-In functions list

    As the version is iteratively updated (if possible), the built-in functions may change at any time. The GetBuildInFunctionList() allows you to view information about the built-in functions supported by the current version.

    Console.WriteLine(STJsonPath.GetBuildInFunctionList().ToString(4));
    @@ -1028,7 +1299,7 @@ 

    STJsonPath

    { "name": "typeof", "demos": [ - "(object) -> typeof('abc')", "(array,bool) -> typeof(['abc',123],true)" + "(object) -> typeof('abc')" ] }, ... @@ -1052,7 +1323,7 @@

    STJsonPath

    } ]
    -

    Custom functions

    +

    Custom functions

    When providing built-in functions for expressions, the authors didn't know what kind of functions the developers expected, so they built a few and then just left them alone. There is no problem that can't be solved by writing code, yes. Let the developers write their own. But how do developers provide functions for STJsonPath?

    STJsonPath.CustomFunctions is a static dictionary. It is used to hold developer-defined functions. Its function signatures are as follows:

    @@ -1104,7 +1375,7 @@

    STJsonPath

    The custom function takes precedence over the built-in function, that is, if there is a function with the same name in the custom function and the built-in function, the custom function is called first.

    -

    Selectors in expressions

    +

    Selectors in expressions

    A selector in an expression returns only the first result selected, not a list of arrays.

    Console.WriteLine(json_src[0].Select("name").ToString(4));
    @@ -1142,7 +1413,7 @@ 

    STJsonPath

    } }
    -

    Select mode

    +

    Select mode

    Normal mode (default):

    Console.WriteLine(json_src.Select("..name", STJsonPathSelectMode.ItemOnly).ToString(4));
    @@ -1201,7 +1472,7 @@ 

    STJsonPath

    } ]
    -

    ParsedTokens

    +

    ParsedTokens

    GetParsedTokens() is used to get how the current STJsonPath string is parsed internally and output as STJson. If you want to write a parser too, it might give you some ideas.

    Console.WriteLine(
    @@ -1290,8 +1561,8 @@ 

    STJsonPath

    ] }
    -

    STJson [Advanced]

    -

    Get value

    +

    STJson [Advanced]

    +

    Get value

    Imagine a scenario where, as a WEB back-end service, we need to process Json data submitted by the front-end, assuming we expect the following Json data:

    {
    @@ -1359,7 +1630,7 @@ 

    STJson [Advanced]

    If the filled strJsonPath will get more than one value, the first value is taken.

    -

    Set value

    +

    Set value

    If we need to construct a Json manually we can add elements via STJson.SetItem(). However, some scenarios seem a bit tricky to use this way, for example, take the query syntax of the ElasticSearch database as an example. We need to construct a data as follows:

    {
    @@ -1432,7 +1703,71 @@ 

    STJson [Advanced]

    Only list selector and slice selector are supported in Set. No other selectors are supported.

    -

    Callback

    +

    STJsonCreator

    +

    Although it is easy to construct a Json data by Set above, it is also possible to do it by Set for multi-layer nested data structure, but the code will become too complicated and it is not possible to visualize the structure of Json data. STJsonCreator is similar to STJsonWriter, but STJsonCreator creates an STJson object directly, which is usually used to create complex Json objects. Still using the ElasticSearch database as an example to simulate a data retrieval.

    +
    +
    var str_json_post = @"
    +{
    +    type: 'student',
    +    names: ['DebugST', 'None'],
    +}";                                         // The web post data.
    +var json_post = STJson.Deserialize(str_json_post);
    +var json_es_query = STJson.Create((c) =>    // Create ES query json.
    +{
    +    c
    +    .SetItem("from", (json_post.GetValue("page", 1) - 1) * json_post.GetValue("size", 10))
    +    .SetItem("size", json_post.GetValue("size", 10))
    +    .Set("query.bool", () =>
    +    {
    +        c
    +        .Set("filter", () =>
    +        {
    +            c.Set("term.type", json_post["type"]);
    +        })
    +        // c.Set(str_path, bool, callback) bool -> if true, create this path.
    +        .Set("should", !json_post["names"].IsNullOrNullValue(), () =>
    +        {
    +            // c.Append(0, 5, 1, (i)=>{ }) => for(int i = 0; i < 5; i+=1) {...}
    +            // c.Append(IEnumerable, callback);
    +            c.Append(json_post["names"], (item) => // => foreach(var item in json_post) {...}
    +            {
    +                c.Set("term.name", item);
    +            });
    +        });
    +    });
    +});
    +Console.WriteLine(json_es_query.ToString(4));
    +/*******************************************************************************
    + *                                [output]                                     *
    + *******************************************************************************/
    +{
    +    "from": 0,
    +    "size": 10,
    +    "query": {
    +        "bool": {
    +            "filter": {
    +                "term": {
    +                    "type": "student"
    +                }
    +            },
    +            "should": [
    +                {
    +                    "term": {
    +                        "name": "DebugST"
    +                    }
    +                },
    +                {
    +                    "term": {
    +                        "name": "None"
    +                    }
    +                }
    +            ]
    +        }
    +    }
    +}
    +
    +

    You can see that the ES database query syntax is easily constructed from the POST request, and the Json data structure remains consistent. Multiple overloads are provided in Set and Append to make the logic more convenient, but of course they are just convenient syntax. The same can be achieved with if and for.

    +

    Callback

    With the Set above are we able to add a coding to each of the hobbies in json_src? It doesn't seem possible. After all, adding data to hobby requires append not set.

    Of course there is not no way around it.

    @@ -1479,7 +1814,7 @@

    STJson [Advanced]

    ... ]
    -

    PathItem

    +

    PathItem

    There is a rather special data structure in STJsonPath.

    [
    @@ -1526,7 +1861,7 @@ 

    STJson [Advanced]

    } ]
    -

    Clone

    +

    Clone

    If you want to add coding to all user hobbies. but don't want to affect the source data, then you can clone a copy of the data for the operation.

    json_src.Clone().Select("*.hobby", (p, j) => {
    @@ -1548,19 +1883,13 @@ 

    STJson [Advanced]

    ... ]
    -

    Aggregation

    +

    Aggregation

    There are some extensions built into STJson for aggregate operations and simple data processing. Combined with STJsonPath, you can do some data manipulation quickly and easily.

    -

    sort

    -

    Sort() is used to sort the data, internally in a Merge Sort manner. This function has the following formal signature:

    -
    - - - - - -
    returnsignaturenote
    STJsonSort()Sort without specifying path. Default is ascending.
    STJsonSort(bool)Sort without specifying path, specifying whether to sort in descending order.
    STJsonSort(params object[])Sort without specifying path or whether to sort in descending order.
    +

    sort

    +

    Sort() is used to sort data, internally by sorting by subsumption, and Sort has several overloads. However, the final version of Sort has the following signature, all overloads, and eventually calls this function.

    +
    +
    public static STJson Sort(this STJson json, bool is_new_instance, STJsonSortCallback callback);
    -

    Sorting without specifying path:

    var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } };
     var json_objs = STJson.FromObject(arr_obj);
    @@ -1569,32 +1898,42 @@ 

    Aggregation

    /******************************************************************************* * [output] * *******************************************************************************/ -[true,1,2,4,5,6,{"aa":"aa"},null] -[6,5,4,2,true,1,{"aa":"aa"},null]
    -
    -

    Are you wondering why the result is the way it is? But before that why don't you wonder why you need to use such an array to sort?

    -

    Inside Sort, a number is obtained for each element and the index of the element is recorded, then the number is sorted and finally an array is reassembled according to the index position to achieve the sorting purpose. The rules for how to get the number of an element are as follows:

    -
    -
    switch (item.ValueType) {
    -    case STJsonValueType.Long:
    -    case STJsonValueType.Double:
    -        d_temp = item.GetValue<double>();
    -        break;
    -    case STJsonValueType.Boolean: // true will convert to 1
    -        d_temp = item.GetValue<bool>() ? 1 : 0;
    -        break;
    -    case STJsonValueType.Datetime:
    -        d_temp = Convert.ToDouble(item.GetValue<DateTime>());
    -        break;
    -    case STJsonValueType.String:
    -        d_temp = item.Value == null ? 0 : item.Value.ToString().Length;
    -        break;
    -    default:    // For other types just let them always be at the end.
    -        d_temp = arr_b_desc[j] ? double.MinValue : double.MaxValue;
    -        break;
    +[null,1,2,4,5,6,true,{"aa":"aa"}]
    +[{"aa":"aa"},true,6,5,4,2,1,null]
    +
    +

    Do you wonder why this is the case? But before that why don't you first wonder why you need such an array to sort? As you can see above Sort needs a callback function, the STJson extension provides two default callback functions which are constructed by the following code:

    +
    +
    private static STJsonSortCallback BuildDefaultSortCallback(bool is_desc)
    +{
    +    return (a, b) =>
    +    {
    +        int n_ret = 0;
    +        if (a.IsNullOrNullValue()) {
    +            n_ret = -1;
    +        } else if (b.IsNullOrNullValue()) {
    +            n_ret = 1;
    +        } else if (a.IsNumber && b.IsNumber) {
    +            n_ret = Convert.ToDouble(a.Value) < Convert.ToDouble(b.Value) ? -1 : 1;
    +        } else if (a.ValueType != b.ValueType) {
    +            n_ret = a.ValueType - b.ValueType;
    +        } else {
    +            switch (a.ValueType) {
    +                case STJsonValueType.Boolean:
    +                    n_ret = (bool)b.Value ? -1 : 1;
    +                    break;
    +                case STJsonValueType.Datetime:
    +                    n_ret = (DateTime)a.Value < (DateTime)b.Value ? -1 : 1;
    +                    break;
    +                case STJsonValueType.String:
    +                    n_ret = string.Compare(a.Value.ToString(), b.Value.ToString());
    +                    break;
    +            }
    +        }
    +        return is_desc ? -n_ret : n_ret;
    +    };
     }
    -

    So if you don't have that weird array then everything will be fine. What if I really want to sort only the numbers inside?

    +

    So if you don't have that weird array then everything will work fine. What if you really want to just sort the numbers in it?

    var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } };
     var json_objs = STJson.FromObject(arr_obj)
    @@ -1607,7 +1946,7 @@ 

    Aggregation

    [1,2,4,5,6] [6,5,4,2,1]
    -

    Specify path for sorting:

    +

    Sort by Path

    Console.WriteLine(json_src.Sort("age").ToString(4));
     /*******************************************************************************
    @@ -1649,60 +1988,7 @@ 

    Aggregation

    Console.WriteLine(json_src.Sort("age", true).ToString(4));
    -

    Multiple fields can also be specified for sorting at the same time:

    -
    -
    Console.WriteLine(json_src.Sort("gender", false, "age", true).ToString(4));
    -/*******************************************************************************
    - *                                [output]                                     *
    - *******************************************************************************/
    -[
    -    {
    -        "name": "Tony",
    -        "age": 16,
    -        "gender": 0,
    -        "hobby": [
    -            "game", "dance"
    -        ]
    -    }, {
    -        "name": "Tom",
    -        "age": 16,
    -        "gender": 0,
    -        "hobby": [
    -            "cooking", "sing"
    -        ]
    -    }, {
    -        "name": "Kun",
    -        "age": 26,
    -        "gender": 1,
    -        "hobby": [
    -            "sing", "dance", "rap", "basketball"
    -        ]
    -    }, {
    -        "name": "Andy",
    -        "age": 20,
    -        "gender": 1,
    -        "hobby": [
    -            "draw", "sing"
    -        ]
    -    }
    -]
    -
    -

    The above is to use gender for ascending order, and then for the same value and then age for descending order. The Sort function parameter rules are as follows:

    -
    -
    Sort(string strJsonPath, bool isDesc[, string strJsonPath, bool isDesc]*);
    -// STLib.Json.STJsonExtension.cs:Sort()
    -public static STJson Sort(this STJson json, params object[] fields) {
    -    var jsonSort = STJson.CreateArray();
    -    for (int i = 0; i < fields.Length; i += 2) {
    -        var j = new STJson()
    -            .SetItem("path", fields[i] == null ? null : fields[i].ToString())
    -            .SetItem("desc", i + 1 < fields.Length ? Convert.ToBoolean(fields[i + 1]) : false);
    -        jsonSort.Append(j);
    -    }
    -    return json.Sort(jsonSort); // internal
    -}
    -
    -

    group

    +

    group

    Group() is used to group the data by specifying path. It is used in the following way:

    Console.WriteLine(json_src.Group("gender").ToString(4));
    @@ -1764,7 +2050,7 @@ 

    Aggregation

    }

    What should I do if I want to do nested Group()? The author does not intend to implement it in Group(), because STJsonPath can do the job.

    -

    terms

    +

    terms

    Terms() is similar to aggregation in the ElasticSearch database, counting the number of occurrences of a field, and like Sort(), it can choose whether to specify path or multiple paths.

    Console.WriteLine(json_src.Terms("hobby", "gender").ToString(4));
    @@ -1814,7 +2100,7 @@ 

    Aggregation

    And is there a possibility that cardinality only counts the number of individuals. But.... What if you want to get the values of the de-duplicated fields? And it seems that Terms() has already been done in all these cases. It's just not sorting. We already have Sort() function, and then a separate order is difficult?

    -
    json_src.Terms("hobby")["hobby"].Sort("count", true);
    +
    json_src.Terms("hobby").ForEach((item)=>item.Sort("count", true));

    What do I have to do if I want to get the top three most popular hobbies for personnel?

    @@ -1832,7 +2118,7 @@

    Aggregation

    "sing", "dance", "basketball" ]
    -

    min, max

    +

    min, max

    Min() Max() is to find the minimum and maximum elements, respectively, and can be specified without or with multiple paths.

    Console.WriteLine(json_src.Min("age").ToString(4));
    @@ -1864,7 +2150,7 @@ 

    Aggregation

    }

    In fact, the effect above is a bit like Group a bit like that, plus a Sort(). It's almost exactly the same. Even the author himself is confused. We should have used Group() + Sort() inside Min/Max() to achieve this. Forget about it. The code is written. But it is clear that Sort() will certainly reduce efficiency. But then again. The small amount of data does not care about efficiency. But what about big data? Big data volume you give me that you use Json array to save ???? Are you sick?

    -

    avg, sum

    +

    avg, sum

    Avg() Sum() is used to calculate the average and the summary, respectively, and can be unspecified or specify multiple keys.

    Console.WriteLine(json_src.Avg("age").ToString(4));
    @@ -1879,7 +2165,7 @@ 

    Aggregation

    }

    The return value of Sum() is the same as Avg(), the only difference is that value is an average and a summary.

    -

    STJsonPath.Name

    +

    STJsonPath.Name

    In the above demo, we have specified a field for data manipulation, and the resulting json will output the field name as a key. But the above does not emphasize that the operation is performed on the specified key, but on the path. It's just that the path is simpler to make it look like key to us.

    What if aggregation is done this way?

    @@ -1929,7 +2215,7 @@

    Aggregation

    ] }
    -

    The END

    +

    The END

    Finally, thank you so damn much for seeing it through to the end, the author is about to go crazy writing it. If you encounter any problems during the use, please inform the author in time if you can. The author will certainly be very promptly recorded, as to change or not ...... We'll talk about it later.

      @@ -1941,55 +2227,61 @@

      The END

    diff --git a/docs/tutorial_en.txt b/docs/tutorial_en.txt index f2d2ade..adaa9ce 100644 --- a/docs/tutorial_en.txt +++ b/docs/tutorial_en.txt @@ -1,35 +1,49 @@ -#T STLib.STJson +#T STLib.Json #O Introduction -`STLib.STJson`is a`Json`parsing library based on the`MIT`open source protocol. powered by [DebugST](https://github.com/DebugST).The pure native implementation of the library does not rely on any library, so it is very lightweight, convenient, and powerful. Since it does not rely on pure native construction, it can be easily transplanted to other programming languages. Of course, because the author is lazy, the probability is not very high. -When designing `STJson`, the author did not have any objection. After all, there was no dispute about the data format of `Json`. When designing `STJsonPath`, the author hesitated, because the author found many controversial places and could not find a formal `RFC` document about `JsonPath`, but only found a draft: +`STLib.Json` is a `Json` parsing library based on the `MIT` open source protocol . Developed by [DebugST](https://github.com/DebugST), the library is implemented natively without any library dependencies , so it is very lightweight , convenient and powerful . Because there is no use of any dependencies built natively, it can be easily ported to other programming languages. Of course, due to the author is very lazy, this probability is not very big, just dream about it, do not take it too seriously. + +So why develop `STJson`? As we all know, `.NET` comes with a parsing library that is too user-friendly, and I'm sure you're not used to using it. And it just so happens that the author always needs to use `JSON` data processing at the moment. Although there are some third-party libraries that can be used, after the author's experience, he thought, forget about doing it himself. There are two main points. + +#US +`JSON5` support is not friendly enough +`JsonPath` support is not friendly enough. +#UE + +In the design of `STJson` the author does not have any objection, after all, `Json` data format does not exist controversial place, of course, in the compatibility of `JSON5` the author still add part of their own ideas, anyway, the current [JSON5](https://json5.org/) is not the official document. + +When designing `STJsonPath`, the authors hesitated because they found a lot of controversy and were unable to find an official `RFC` document for `JsonPath`, only a draft: [JSONPath: Query expressions for JSON](https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-03.html) +Although some of the doubts in the draft can also be solved, the author still has some doubts, after all, it is not an official document. Before development the author was also curious about other `JsonPath` open source libraries and how other libraries solved the controversial areas. Unfortunately, the problem persists. No library seems to be willing to be compatible with this draft. Since there is no official `RFC` documentation for `JsonPath`, the authors will try to be as compatible as possible with the draft while adding some of their own ideas. -Although some doubts in the draft can also be solved, the author still has some doubts. After all, it is not an official document. Before development, the author was also curious about other open source libraries of `JsonPath`, and how other libraries resolve controversial issues. Unfortunately, the problem still exists. No developer seems willing to refer to this draft. Since there is no formal RFC document for `JsonPath`, the author also wants to establish his own portal. However, the author will not ignore this draft. The author will try to be compatible with the draft as much as possible, and will also add some of his own ideas. +#O The question of Path -#O Path question +`JsonPath` is derived from the use of `XPath`, which is known as the `XML` Path Language, a language used to determine the location of a part of an `XML` document. Obviously `JsonPath` is used for `Json`. Both of them are used in almost the same way, but the differences in the `XML` and `Json` data formats are bound to have differences. +For example, there are no special characters in the element name of `XML`, but the `Key` of `Json` can be any string, if there is a bit of `Json` data: -`JsonPath` is derived from the use of `XPath`, which is known to be the XML Path Language, a language used to determine the location of a part of an `XML` document. Obviously `JsonPath` is used for `Json`. They are both used in almost the same way, but the differences between the two data formats `XML` and `Json` are bound to have their differences. -For example, there are no special characters in the element name of `XML`, but the `Key` of `Json` can be any string, if there is `Json` data: #CS cs { "aa": { "bb": "cc" } } #CE -In `JsonPath` we can get the value `cc` by the path `aa.bb` without any problem, what if the `Json` data is replaced by the following: + +In `JsonPath` we can get the value `cc` by the path `aa.bb` and there is no problem, what if the `Json` data is replaced with the following: + #CS cs { "a.a": { "bb": "cc" } } #CE -Obviously the value `cc` is not available through `a.a.bb`. Maybe normally `Json` represents an object, and objects don't have such strange properties. But for the data format of `Json`, its `Key` can be any character. Even if you pass `{OBJ_NAME}.a.a.bb` in the browser, you will only get an error. But the way the browser accesses the `Json` element is not just through `. `, there is also the indexer `{OBJ_NAME}['a.a']['bb']` that can get the correct value. -The use of `'` or `"` is allowed in `STJsonPath`, which is marked as `String` in the lexical parser (STJsonPathTokenizer.cs), and if this string is in the scope of a non-expression, the parser (STJsonPathParser.cs) will re-tag it as `Property ` as an index to avoid special strings that can't be handled, and strings support `\\` for escaping. So you can use `'a.a'.bb` in `STJsonPath` to get the correct value. And a similar treatment is indeed mentioned in the draft. -#O Expression question -In the draft there is mention of supporting expressions and that there are two types of expressions, `()` and `? ()`. They stand for `ordinary expressions` and `filtered expressions` respectively. And according to the author's understanding, `ordinary expression` is used to calculate a value and use this value as part of `JsonPath`, such as `$.books[(@.length - 1)]` which can be seen in many cases. It is possible to perform recursion on `Json` elements when executing `JsonPath`, where `@` is a dynamic variable that indicates the `Json` element currently being processed during the recursion, and according to the surface meaning of the expression is trying to get the last element in `books`. -But how does `@.length` get executed? Where does `length` come from? If `@` is an array, the author can tentatively understand that it is finding the length of the array? But what if `@` is an object? -Since it is an expression, what kind of syntax can be written in `()`? What about the syntax? About all these queries the author has used his own implementation, which will be introduced in a later tutorial. +Obviously the value `cc` is not available through `a.a.bb`. Perhaps normally `Json` represents some object, and objects don't have such strange properties. But for the `Json` data format, its `Key` can be any character. Even if you pass `{OBJ_NAME}.a.a.bb` in the browser, you'll just get an error. But browsers don't access `Json` elements only by `. `, but also the indexer `{OBJ_NAME}['a.a']['bb']` to get the correct value. +The use of `'` or `"` is allowed in `STJsonPath`, which will be marked as `String` in the lexical parser (STJsonPathTokenizer.cs), and if this string is in the scope of a non-expression, the parser (STJsonPathParser.cs) will re-tag it as `Property ` to be used as an index, thus avoiding cases where special strings can't be handled, and strings support `\\` for escaping. So you can use `'a.a'.bb` in `STJsonPath` to get the correct value. And similar handling is indeed mentioned in the draft. +#O The question of expression + +In the draft there is a reference to support for expressions and that there are two types of expressions, `()` and `? ()`. These represent `regular expressions` and `filter expressions` respectively. According to the author's understanding, `regular expression` is used to calculate a value and use this value as part of `JsonPath`, such as `$.books[(@.length - 1)]` in many cases. When executing `JsonPath` it is possible to perform a cascading recursion on the `Json` elements, where `@` is a dynamic variable indicating the `Json` element currently being processed during the recursion, and according to the expression's apparent meaning it is trying to get the penultimate element in `books`. +But how does `@.length` get executed? Where does `length` come from? If `@` is an array, the author can tentatively understand that he is asking for the length of the array? But what if `@` is an object? +Since it is an expression, what kind of syntax can be written in `()`? What about the grammar? Regarding all these queries the author has used his own implementation, which will be presented in a later tutorial. #T STJson API -#O Data mapping + +#O Data type #TS .Net|STJsonValueType|.Net|STJsonValueType byte|Long|sbyte|Long @@ -48,68 +62,72 @@ Array|Array|ICollection|Array IDectionary|Object|object|Object #TE -`STJson` covers the common basic data types, even if they are not included, then the properties of the object will be recurred by performing reflection in the end. +`STJson` covers common basic data types, and even if they are not included, then the properties of the object will eventually be recursed by performing reflection. -#O Static functions +#O Static methods #HS goldenrod|black -`(+n)` in the following indicates that there are multiple overloads. +In the following, `(+n)` means that there is more than one overloading. #HE #TS return|signature|note -string|Serialize(+n)|Serialize an `object` object to a string. -STJson|Deserialize(string)|Convert a string to `STJson` object order. +string|Serialize(+n)|Serializes an `object` object to a string. +STJson|Deserialize(string)|Convert a string to an `STJson` object. T|Deserialize(+n)|Converts an object or string to a target object. -STJson|STJson CreateObject()|Create an empty object。 -STJson|STJson CreateArray(params object[])|Create an array object。 -STJson|FromObject(+n)|Converts an object to `STJson`. -string|Format(+n)|Format a `Json` string. -void|AddCustomConverter(+n)|Add custom type converter. -void|RemoveCustomConverter(+n)|Remove custom type converter - +STJsonWriter|Write|Create an `STJsonWriter` object. +STJsonReader|Read|Create an `STJsonReader` object. +STJson|Create(STJsonCreator)|Create an `STJson` object. +STJson|CreateObject()|Creates an `Null` object. +STJson|CreateArray(params object[])|Creates an `array` object.。 +STJson|FromObject(+n)|Convert an object to `STJson`. +string|Format(+n)|Formats a `Json` string. +void|AddCustomConverter(+n)|Custom type converters. +void|RemoveCustomConverter(+n)|Remove the custom converter. #TE -#O Non-Static functions +#O Non-Static methods #TS return|signature|note -STJson|SetItem(+n)|Adds a key-value pair to the object and returns itself. -STJson|SetKey(string)|Adds a `key` to the object and returns the target object. +STJson|SetItem(+n)|Add a key-value pair to the object and returns itself. +STJson|SetKey(string)|Add a `key` to the object and returns the target object. +void|GetValue(+n)|Get the value of the target object. void|SetValue(+n)|Set the value of the target object. -STJson|Delete(string)|Removes a `key` from the object and returns the target object. -STJson|Append(+n)|Adds one or some elements to the array object and returns itself. -STJson|Insert(+n)|Inserts an element into the array object and returns itself. -STJson|RemoveAt(int nIndex)|Removes an index from the array object. -void|Clear()|Clear all child elements. -IEnumerator|GetEnumerator()|Get all the child elements of the current element. -STJson|Clone|Clone the current element. +STJson|Remove(string)|Remove a `key` from an object and returns the target object. +STJson|Append(+n)|Add an element or elements to an array object and returns itself. +STJson|Insert(+n)|Insert an element or elements to an array object and returns itself. +STJson|RemoveAt(int nIndex)|Remove an index from an array object. +void|Clear()|Clear all elements. +IEnumerator|GetEnumerator()|Get all elements. +STJson|Clone|Clone current `STJson`。 #TE -#O Extended functions +#O Extensions methods #TS return|signature|note +bool|IsNullOrNullValue|Check current `STJson` is `null` or the `Undefined`. STJson|Set(+n)|Set the object according to the path (STJsonPath). -string|GetValue(+n)|Get the string value of the object (value-type only) -T|GetValue(+n)|Get the value of the object (value-type only) -STJson|Select(+n)|Filter data in an object. -STJson|SelectFirst(+n)|Filter data in the object and select the first result. -STJson|SelectLast(+n)|Filters through the data in the object and selects the last result. +string|GetValue(+n)|Get the string value of the object (value type only). +T|GetValue(+n)|Get value from value(value type only). +STJson|Select(+n)|Filter the data in the object. +STJson|SelectFirst(+n)|Filter the data in the object and check the first result. +STJson|SelectLast(+n)|Filter the data in the object and check the last result. STJson|Group(+n)|Groups the specified fields. -STJson|Terms(+n)|Count the number of values for the specified field. -STJson|Sort(+n)|Sort the specified fields. -STJson|Min(+n)|Tally the minimum value of the specified field. -STJson|Max(+n)|Tally the maximum value of the specified field. -STJson|Sum(+n)|Tally the total number of the specified fields. -STJson|Avg(+n)|Tally the average of the specified fields. +STJson|Terms(+n)|Counts the number of values in the specified field. +STJson|Sort(+n)|Sort the array. +STJson|Min(+n)|Get the minimum value from the array. +STJson|Max(+n)|Get the maximum value from the array. +STJson|Sum(+n)|Get the summary from the array. +STJson|Avg(+n)|Get the average value from the array. #TE #O Fields #TS type|name|note -string|Key|the `Key` of the parent element of the current `STJson -object|Value|the value of the current `STJson`, or `null` if the current `STJson` is not of value type -bool|IsNullObject|whether the current `STJson` is a null element, i.e. the data type of the current `STJson` value cannot be determined. +string|Key|The `Key` of the current `STJson` parent element. +object|Value|The value of the current `STJson`, or `null` if the current `STJson` is not a value type. +bool|IsNullValue|Whether the current `STJson` is an empty element, i.e. it is not possible to determine the data type of the current `STJson` value. int|Count|The number of child elements contained in the current `STJson`. -STJsonValueType|ValueType|(enumeration) The data type of the current element. +STJsonValueType|ValueType|(Enumeration) The data type of the current element. #TE `STJsonValueType` is the following value: `Undefined` `String` `Boolean` `Long` `Double` `Datetime` `Array` `Object` @@ -121,12 +139,20 @@ STJson STJson[int] STJson STJson[string] #UE +#O Other object + +#TS name|note +STJsonCreator|Used to create a complex `STJson` object. +STJsonReader|Used to dynamically parse objects from a `TextReader`. Typically used to read a `Json` with larger data from a file. +STJsonWriter|Used to write a `Json` string to a `TextWriter`. Typically used to write a large and complex `Json` data directly to a file. +#TE + #T STJson [Basic] -`STJson` is an intermediate data type that is a bridge between `string` and `object` and is very convenient to use, e.g: +`STJson` is an intermediate data type that bridges between `string` and `object` and is very convenient to use, for example: #CS cs var st_json = new STJson() - .SetItem("number", 0) // The function returns itself, so it can be operated continuously + .SetItem("number", 0) // The function returns itself, so it can operate continuously. .SetItem("boolean", true) .SetItem("string", "this is string") .SetItem("datetime", DateTime.Now) @@ -159,37 +185,37 @@ Console.WriteLine(st_json.ToString(4)); // 4 -> indentation space count #CE #HS pink|black -When executing `var st_json = new STJson()`, `st_json` is the empty element, i.e. `st_json.IsNullObject = true`. Because at this point it is not possible to determine whether `st_json` is an `object` or an `array` or a `value`. -There are no objects like `JArray` or `JObject` in `STJson`, `STJson` can be either `Array` or `Object`. `STJson` has two indexers `[int]``[string]`. +When executing `var st_json = new STJson()`, `st_json` is the empty element, i.e. `st_json.IsNullValue = true`. Because at this point it is not possible to determine whether `st_json` is an `object` or an `array` or a `value`. +There is no object like `JArray` or `JObject` in `STJson`, `STJson` can be either `Array` or `Object`. `STJson` has two indexers `[int]` `[string]`. #HE #CS cs var json_1 = new STJson(); -Console.WriteLine("[json_1] - " + json_1.IsNullObject + " - " + json_1.ValueType); +Console.WriteLine("[json_1] - " + json_1.IsNullValue + " - " + json_1.ValueType); var json_2 = new STJson(); json_2.SetItem("key", "value"); -Console.WriteLine("[json_2] - " + json_2.IsNullObject + " - " + json_2.ValueType); +Console.WriteLine("[json_2] - " + json_2.IsNullValue + " - " + json_2.ValueType); var json_3 = new STJson(); json_3.Append(1, 2, 3); -Console.WriteLine("[json_3] - " + json_3.IsNullObject + " - " + json_3.ValueType); +Console.WriteLine("[json_3] - " + json_3.IsNullValue + " - " + json_3.ValueType); var json_4 = new STJson(); json_4.SetValue(DateTime.Now); -Console.WriteLine("[json_4] - " + json_4.IsNullObject + " - " + json_4.ValueType); +Console.WriteLine("[json_4] - " + json_4.IsNullValue + " - " + json_4.ValueType); var json_5 = STJson.CreateArray(); // made by static function -Console.WriteLine("[json_5] - " + json_5.IsNullObject + " - " + json_5.ValueType); +Console.WriteLine("[json_5] - " + json_5.IsNullValue + " - " + json_5.ValueType); var json_6 = STJson.CreateObject(); // made by static function -Console.WriteLine("[json_6] - " + json_6.IsNullObject + " - " + json_6.ValueType); +Console.WriteLine("[json_6] - " + json_6.IsNullValue + " - " + json_6.ValueType); var json_7 = STJson.FromObject(12); // made by static function -Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType); +Console.WriteLine("[json_3] - " + json_7.IsNullValue + " - " + json_7.ValueType); /******************************************************************************* * [output] * *******************************************************************************/ -[json_1] - True - None +[json_1] - True - Undefined [json_2] - False - Object [json_3] - False - Array [json_4] - False - Datetime @@ -199,10 +225,10 @@ Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType #CE #HS goldenrod|black -When `ValuteType = Array`, `SetItem(+n)` cannot be called, and when `ValueType = Object`, `Append(+n)` cannot be called. But `ValueType` can be forced to change by `SetValue(+n)`. +Normally `SetItem(+n)` sets the `ValueType` to `Object`, and `Append(+n)` `Insert(+n)` sets the `ValueType` to `Array`. #HE -As mentioned above `STJson` has two indexers through which you can access them, or get the values. +As mentioned above `STJson` has two indexers that can be used to access them, or get values. #CS cs var json_temp = STJson.CreateArray() .SetItem("string", "this is string") @@ -221,8 +247,8 @@ this is string #CE #O object -> string -By the above example maybe you already know how to convert an object to `string`, by `STJson.FromObject(object).ToString(+n)`, but is it possible that it actually doesn't need to be so troublesome? For example: `STJson.Serialize(+n)` will do? -In fact `STJson.Serialize(+n)` would be more efficient because it converts the object directly to a string, rather than converting it to `STJson` and then to a string. +With the above example maybe you already know how to convert an object to `string`, by `STJson.FromObject(object).ToString(+n)`, but is it possible that it doesn't really have to be so tedious? For example, `STJson.Serialize(+n)` would be fine???? +Actually `STJson.Serialize(+n)` would be more efficient, because it converts the object directly to a string, instead of converting to `STJson` and then to a string. #CS cs Console.WriteLine(STJson.Serialize(new { key = "this is test" })); @@ -242,24 +268,50 @@ Console.WriteLine(STJson.Serialize(new { key = "this is test" }, 4)); "key": "this is test" } #CE -In fact formatting the output is done by calling the static function `STJson.Format(+n)`. If you don't like the author's built-in formatting style, you can write a formatting function of your own. Or go modify the source file `STJson.Statics.cs:Format(string,int)`. + +Or you can serialize directly into a `TextWriter`: +#CS cs +STJson.Serialize(new { key = "this is test" }, new StreamWriter("./test.json"))); +#CE #O string -> object -The code doesn't actually convert `string` to `object` directly. It has to parse the string to make sure it's a properly formatted `Json` before it does that. But by the time this is done, you'll already have an `STJson` object. Finally, `STJson` is converted to `object` again. +In fact the code does not directly convert `string` to `object`. That's because the string has to be parsed before that to make sure it's a properly formatted `Json`. But by the time it's done, it's already got an `STJson` object. Finally the `STJson` is converted to `object`. So you will see the following file in the source code `STLib.Json.Converter`: -`ObjectToSTJson.cs` `ObjectToString.cs` `STJsonToObject.cs` `StringToSTJson.cs` -There is no `StringToObject.cs` file inside, and the source code of `STJson.Deserialize(+n)` is as follows: +`ObjectToSTJson.cs` `ObjectToString.cs` `STJsonToObject.cs` +There is no `StringToObject.cs` file in there, and the source code for `STJson.Deserialize(+n)` is as follows: #CS cs public static T Deserialize(string strJson, +n) { - var json = StringToSTJson.Get(strJson, +n); + var json = STJsonParser.Parse(strJson); return STJsonToObject.Get(json, +n); } #CE +#O STJson -> object + +I'm sure the author doesn't need to explain how to convert a string to an object and the reader should know how to handle it, but it's worth stating here that `STJson` can be appended to an object to enable localized updates. +#CS cs +public class TestClass { + public int X; + public int Y; +} + +TestClass tc = new TestClass() { + X = 10, + Y = 20 +}; +STJson json_test = new STJson().SetItem("Y", 100); +STJson.Deserialize(json_test, tc); +Console.WriteLine(STJson.Serialize(tc)); +/******************************************************************************* + * [output] * + *******************************************************************************/ + {"X":10,"Y":100} +#CE + #O STJsonConverter -Although there are a lot of data type conversions built into `STJson`, even data types that are not available are treated as `object` for recursive processing. But sometimes the situation is not very friendly. For example: +Although there are a lot of type conversions built into `STJson`, and even types that aren't there are recursive. But sometimes the situation is not very friendly. For example: #CS cs Rectangle rect = new Rectangle(10, 10, 100, 100); Console.WriteLine(STJson.Serialize(rect, 4)); @@ -288,7 +340,7 @@ Console.WriteLine(STJson.Serialize(rect, 4)); "IsEmpty": false } #CE -Obviously, this result is too complicated because all the fields of `Rectangle` are recursive. But what if this is the case? +Obviously, this result is overly complicated because all the fields of `Rectangle` are recursively out. But what if this is the case? #CS cs public class RectangleConverter : STJsonConverter { @@ -332,10 +384,10 @@ Console.WriteLine(rect); {X=10,Y=10,Width=200,Height=200} #CE #HS goldenrod|black -where `bProcessed` is passed in as `true` by default, and the default processing is used when the upper-level function gets `false`. +Where `bProcessed` is passed in with a default value of `true`, and the default processing is used when the higher level function gets `false`. #HE -The `STJsonConverter` provides the `Attribute` class, which can also be used to tag object attributes. +`STJsonConverter` provides the `Attribute` class, which can also be used to mark object attributes. #CS cs public class Test{ [STJsonConverter(typeof(RectangleConverter))] @@ -363,7 +415,7 @@ public abstract class STJsonConverter #O STJsonAttribute -Maybe you don't want to output all the attributes when serializing, then you can control it with `STJsonAttribute`. +Maybe you don't want to output all the attributes when serializing, then you can control that with `STJsonAttribute`. #CS cs [STJson(STJsonSerilizaMode.Include)] // optional @@ -399,7 +451,7 @@ Console.WriteLine(str); #O STJsonSetting -`STJsonSetting` is used to add some personalized settings to the serialization or deserialization. Originally the settings were global. But with the increase of some setting items, the author thinks that the global setting is too sticky, so the independent `STJsonSetting` is used for decoupling. At the same time the independent setting class can also facilitate the subsequent version of the feature expansion, of course the probability of subsequent versions is not very large. Unless the author does not want to be a salty fish. +`STJsonSetting` is used to add some personalized settings in serialization or deserialization. Originally, the settings were global. But with the increase of some settings, the author thinks the global settings are too coupled, so `STJsonSetting` is independent to decouple them. And the independent settings class can also facilitate the expansion of functionality in subsequent versions, of course, the probability of subsequent versions is not very large. Because the author is very lazy. #CS cs var stu = new Student() { Name = "Tom", @@ -428,12 +480,294 @@ STJson.Deserialize(str); #CE #HS pink|black -The priority of `Attribute` is greater than that of `STJsonSetting`. +`Attribute` has a higher priority than `STJsonSetting`. #HE -#O json_src +#O JSON5 -In the next tutorials we will use some test data, which are as follows: +Support for [JSON5](https://json5.org/) is provided in `STJson(3.0)`, and the author provides some more convenient ideas, as exemplified by the following text: +#CS cs,json5.txt +{ + "normal_key": "This is a normal key and value.", // this is a line comment。 + /* + this is a block comment。 + */ + str_value_1: 'string_1', // For a key the ["] is not must. + str_value_2: "string_2", // For a string you can use ['] or ["] + int_numbers: [ // Hex number + 0x123, -0x123, +123, -123 + ], + 'float_numbers':[ + .123, 123., -.123, +123., +123E-2 + ], + // From json 5 -> https://json5.org + string_1: "string_1.\
    string_2.\
    |<- some space", + + // But in STJson you can just use [\r\n] line breaks and keep the line breaks. + string_2: "string_1.
    string_2.
    |<- some space", + + // Or even use it that way. The authors consider JSON5's line continuation to be unfriendly. + // After a line break via [\], the new line cannot be preceded by whitespace or it will be parsed into the string. + // So STJson allows consecutive strings and eventually merges them into a single string. + string_3: + "string_1." + "string_2." + "\r\nstring_3.", + array:[ + 123,true,"string_1.""string_2.","string_3", + ], +} +#CE + +#CS cs +var str_file = "./json5.txt"; +var str_json = File.ReadAllText(str_file, Encoding.UTF8); +var json = STJson.Deserialize(str_json); +Console.WriteLine(json.ToString(4)); +/******************************************************************************* + * [output] * + *******************************************************************************/ +{ + "normal_key": "This is a normal key and value.", + "str_value_1": "string_1", + "str_value_2": "string_2", + "int_numbers": [ + 291, + -291, + 123, + -123 + ], + "float_numbers": [ + 0.123, + 123, + -0.123, + 123, + 1.23 + ], + "string_1": "string_1.string_2. |<- some space", + "string_2": "string_1.\r\nstring_2.\r\n |<- some space", + "string_3": "string_1.string_2.\r\nstring_3.", + "array": [ + 123, + true, + "string_1.string_2.", + "string_3" + ] +} +#CE + + +#O STJsonReader + +`STJsonReader` can take characters from a `TextReader` and parse them dynamically, usually used for parsing large text data. E.g. load a `Json` data from a file or stream via `StreamReader`. +#CS cs +string str_json = @" +{ + name: 'DebugST', + language: ['C#', 'JS'], + address: { + country: 'China', + province: 'GuangDong', + city: 'ShenZhen' + } +}"; +using (var reader = new STJsonReader(new StringReader(str_json))) { + foreach (var v in reader) { + //Console.WriteLine(STJson.Serialize(v, 4)); + Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]"); + } +} +/******************************************************************************* + * [output] * + *******************************************************************************/ +name: DebugST - [String] +language: [...] - [Array] +language[0]: C# - [String] +language[1]: JS - [String] +address: {...} - [Object] +address.country: China - [String] +address.province: GuangDong - [String] +address.city: ShenZhen - [String] +#CE + +By default `STJsonReader` recurses all data. Change the code slightly: +#CS cs +foreach (var v in STJson.Read(new StringReader(str_json))) { + if (v.Path == "language") { + Console.WriteLine(v.GetSTJson().ToString(4)); + } else { + Console.WriteLine(v.Path + ": " + v.Text + " - [" + v.ValueType + "]"); + } +} +/******************************************************************************* + * [output] * + *******************************************************************************/ +name: DebugST - [String] +[ + "C#", + "JS" +] +address: {...} - [Object] +address.country: China - [String] +address.province: GuangDong - [String] +address.city: ShenZhen - [String] +#CE + +`GetSTJson()` will keep fetching data backwards from the current position until it stops fetching a complete `STJson` object. And as you can see, in subsequent iterations, `language` has been skipped because he was fetched by `GetSTJson()`. + +#HS pink|black +`STJsonReader` performance is much lower than `STJson.Deserialize(+n)`, and there is no intention to optimize `STJsonReader` at this time. The authors believe that `STJsonReader` is only used in special scenarios, however special scenarios are not considered for performance. +#HE + +#HS goldenrod|black +And since `STJsonReader` uses dynamic parsing, and `STJson` is not parsed by a state machine, the author does not intend to do strict `Json` data format checking. So the following code also works fine and is equivalent to `str_json` above. +#HE +#CS cs +string str_json = @" +{ + name 'DebugST' + language: ['C#' 'JS'], + address: { + country: 'China',,,,,,, + province: 'GuangDong',,,, + city:::::::::::: 'ShenZhen'"; +#CE +#CS cs +string str_json = @" +{ + name: 'DebugST', + language: ['C#', 'JS'], + address: { + country: 'China',,,,,,, + province: 'GuangDong',,,, + city:::::::::::: 'ShenZhen' + }}}}}}}}}} +}1234567890"; +#CE + +#HS pink|black +When `STJsonReader` is dynamically parsed, the author ignores `:``,`. This way, when reading a `key-value pair`, you only need to read two `tokens`. The first one is used as the `key` and the second one is used as the `value`. Part of the source code is as follows: +#HE + +#CS cs +private STJsonToken GetNextFilteredToken() +{ + foreach (var v in m_token_reader) { + switch (v.Type) { + case STJsonTokenType.KVSplitor: // : + case STJsonTokenType.ItemSplitor: // , + continue; + default: + return v; + } + } + return STJsonToken.None; +} + +private STJsonReaderItem GetNextObjectKV() +{ + var token_key = this.GetNextFilteredToken(); + switch (token_key.Type) { + case STJsonTokenType.None: + return null; + case STJsonTokenType.ObjectEnd: + this.PopStack(); + return this.GetNextItem(); + case STJsonTokenType.Symbol: + case STJsonTokenType.String: + break; + default: + throw new STJsonParseException(token_key); + } + m_current_stack.Key = token_key.Value; + var token_val = this.GetNextFilteredToken(); + if (token_val.Type == STJsonTokenType.None) { + return null; + //throw new Exception("error"); + } + var item = new STJsonReaderItem(this, token_val) + { + ParentType = STJsonValueType.Object, + Key = token_key.Value, + Text = token_val.Value + }; + return this.CheckValueToken(item, token_val); +} + +private STJsonReaderItem GetNextArrayItem(){...} +#CE + +#O STJsonWriter + +`STJsonWriter` plays the opposite role to `STJsonReader`, `STJsonWriter` can be used to construct and write `Json` strings in real time directly to a `TextWriter` or stream. +#CS cs +StringWriter sw = new StringWriter(); +//using (var writer = new STJsonWriter(sw)) { +// writer.StartWithArray((w) => +// { +// // writer == w +// }); +//} +STJson.Write(sw, 4).StartWithArray((w) => +{ + for (int i = 0; i < 1; i++) { + Console.WriteLine("Level:" + w.Level); + w.CreateObject(() => + { + Console.WriteLine("Level:" + w.Level); + w + .SetItem("name", "DebugST") + //.SetItem("language", STJson.CreateArray("C#", "JS")) + //.SetItem("language", new string[] { "C#", "JS" }) + .SetArray("language", () => + { + Console.WriteLine("Level:" + w.Level); + w + .Append("C#") + .Append("JS"); + }) + .SetObject("address", () => + { + Console.WriteLine("Level:" + w.Level); + w + .SetItem("country", "china") + .SetItem("province", "GuangDong") + .SetItem("city", "ShenZhen"); + }); + }); + } +}); +Console.WriteLine(sw.ToString()); +/******************************************************************************* + * [output] * + *******************************************************************************/ +Level:1 +Level:2 +Level:3 +Level:3 +[ + { + "name": "DebugST", + "language": [ + "C#", + "JS" + ], + "address": { + "country": "china", + "province": "GuangDong", + "city": "ShenZhen" + } + } +] +#CE + +#HS skyblue|black +`STJsonWriter` uses a hierarchical approach to callbacks in order to be consistent with the data structure hierarchy of the target `Json`. As shown above, `STJsonWriter` manages the level of callback functions internally. +#HE + +#O json_src [Important] + +In the next tutorial we will use some test data, which is as follows: #CS cs,test.json [{ @@ -458,18 +792,18 @@ In the next tutorials we will use some test data, which are as follows: ] }] #CE -To load it into the program: +Load it into the program: #CS cs var json_src = STJson.Deserialize(System.IO.File.ReadAllText("./test.json")); #CE #HS pink|black -The appearance of `json_src` in the subsequent cases is the above object. +Subsequent cases where `json_src` appears are the above objects. #HE #T STJsonPath -In the source code `STJsonExtension.cs` the functionality of `STJson` is extended and some of the functionality of `STJsonPath` is integrated in it. So there is no dependency on `STJsonPath` in the original code of `STJson`, and `STJson` can be used independently. But `STJsonPath`, as a helper class of `STJson`, needs to depend on `STJson`. +In the source code `STJsonExtension.cs`, the functionality of `STJson` is extended, which integrates some functionality of `STJsonPath`. So there is no dependency on `STJsonPath` in the original `STJson` code, `STJson` can be used independently. But `STJsonPath` is a helper class of `STJson`, it needs to depend on `STJson`. #O Selector @@ -536,7 +870,7 @@ Console.WriteLine(json_temp.Select("'a.b'")); [] ["this is a test"] #CE -Support for `\\` in strings for escaping:`\\r``\\n``\\t``\\f``\\b``\\a``\\v``\\0``\\x..``\\u...``\\.` +Support for `\\` in strings for escaping:`\\r``\\n``\\t``\\f``\\b``\\a``\\v``\\0``\\x..``\\u....``\\.` #O Wildcard character @@ -907,76 +1241,33 @@ Console.WriteLine(STJsonPath.TestExpression( #O Build-In functions #TS return|signature|note -string|typeof(+n)|Get the data type. -string|str(+n)|Convert to string. -string|upper(+n)|Convert to uppercase. -string|lower(+n)|Convert to lowercase. - -long|len(+n)|Get the length of a string or an array. -long|long(+n)|Convert to integer. -double|double(+n)|Convert to double. -long or double|abs(+n)|Get the absolute value. -long|round(+n)|Round up to round down. -long|ceil(+n)|Round up. +string|typeof(object)|Get the data type. +string|str(object)|Convert to string. +string|upper(string)|Convert to uppercase. +string|lower(string)|Convert to lowercase. + +long|len(string or array)|Get the length of a string or an array. +long|long(string or number)|Convert to integer. +double|double(string or number)|Convert to double. +number|abs(number)|Get the absolute value. +long|round(number)|Round up to round down. +long|ceil(number)|Round up. + +long or double|max(array)|Get the maximum value. +long or double|min(array)|Get the minimum value. +long or double|avg(array)|Get the average. +long or double|sum(array)|Get the summary. + +string|trim(string)|Crop the specified characters at both ends of the string. +string|trims(string)|Cuts the character at the beginning of the string. +string|trime(string)|Cuts the character at the end of the string. +string_array|split(+1)|split the string. +long or string|time(+n)|Get or format the timestamp. #TE `typeof` has the following return values: `string` `long` `double` `boolean` `array` `object` `undefined` -The above functions can be signed with the following signature: -#US -fn(object) -fn(object[]) -fn(object,bool) -fn(object[],bool) -#UE -where `bool` indicates whether the result is expected as an array, if `true` then the result is output as an array, and when `object` is an array, the data in it is recursive. -#CS cs -Console.WriteLine(STJsonPath.TestExpression( - null, - null, - "typeof([1+2+3,'abc'], false)" - ).SelectFirst("return").ToString(4)); -/******************************************************************************* - * [output] * - *******************************************************************************/ -{ - "bool": true, - "value_type": "String", - "value": "array" -} -// ============================================================================= -Console.WriteLine(STJsonPath.TestExpression( - null, - null, - "typeof([1+2+3,'abc'], true)" - ).SelectFirst("return").ToString(4)); -/******************************************************************************* - * [output] * - *******************************************************************************/ -{ - "bool": true, - "value_type": "Array", - "items": [ - "long", "string" - ] -} -#CE -Different functions use different default values for the second parameter. The following are some other functions that the reader can test the effect themselves by `TestExpression()`. - -#TS return|signature|note -long or double|max(+1)|Get the maximum value. -long or double|min(+1)|Get the minimum value. -long or double|avg(+1)|Get the average. -long or double|sum(+1)|Get the summary. - -string|trim(+n)|Crop the specified characters at both ends of the string. -string|trims(+n)|Cuts the character at the beginning of the string. -string|trime(+n)|Cuts the character at the end of the string. -string_array|split(+1)|split the string. -long or string|time(+n)|Get or format the timestamp. -#TE - #O Build-In functions list As the version is iteratively updated (if possible), the built-in functions may change at any time. The `GetBuildInFunctionList()` allows you to view information about the built-in functions supported by the current version. @@ -989,7 +1280,7 @@ Console.WriteLine(STJsonPath.GetBuildInFunctionList().ToString(4)); { "name": "typeof", "demos": [ - "(object) -> typeof('abc')", "(array,bool) -> typeof(['abc',123],true)" + "(object) -> typeof('abc')" ] }, ... @@ -1265,6 +1556,7 @@ Console.WriteLine( } #CE + #T STJson [Advanced] #O Get value @@ -1412,6 +1704,75 @@ Console.WriteLine(json.ToString(4)); Only `list selector` and `slice selector` are supported in `Set`. No other selectors are supported. #HE +#O STJsonCreator + +Although it is easy to construct a `Json` data by `Set` above, it is also possible to do it by `Set` for multi-layer nested data structure, but the code will become too complicated and it is not possible to visualize the structure of `Json` data. `STJsonCreator` is similar to `STJsonWriter`, but `STJsonCreator` creates an `STJson` object directly, which is usually used to create complex `Json` objects. Still using the `ElasticSearch` database as an example to simulate a data retrieval. + +#CS cs +var str_json_post = @" +{ + type: 'student', + names: ['DebugST', 'None'], +}"; // The web post data. +var json_post = STJson.Deserialize(str_json_post); +var json_es_query = STJson.Create((c) => // Create ES query json. +{ + c + .SetItem("from", (json_post.GetValue("page", 1) - 1) * json_post.GetValue("size", 10)) + .SetItem("size", json_post.GetValue("size", 10)) + .Set("query.bool", () => + { + c + .Set("filter", () => + { + c.Set("term.type", json_post["type"]); + }) + // c.Set(str_path, bool, callback) bool -> if true, create this path. + .Set("should", !json_post["names"].IsNullOrNullValue(), () => + { + // c.Append(0, 5, 1, (i)=>{ }) => for(int i = 0; i < 5; i+=1) {...} + // c.Append(IEnumerable, callback); + c.Append(json_post["names"], (item) => // => foreach(var item in json_post) {...} + { + c.Set("term.name", item); + }); + }); + }); +}); +Console.WriteLine(json_es_query.ToString(4)); +/******************************************************************************* + * [output] * + *******************************************************************************/ +{ + "from": 0, + "size": 10, + "query": { + "bool": { + "filter": { + "term": { + "type": "student" + } + }, + "should": [ + { + "term": { + "name": "DebugST" + } + }, + { + "term": { + "name": "None" + } + } + ] + } + } +} +#CE + +You can see that the `ES` database query syntax is easily constructed from the `POST` request, and the `Json` data structure remains consistent. Multiple overloads are provided in `Set` and `Append` to make the logic more convenient, but of course they are just convenient syntax. The same can be achieved with `if` and `for`. + + #O Callback With the `Set` above are we able to add a `coding` to each of the hobbies in `json_src`? It doesn't seem possible. After all, adding data to `hobby` requires `append` not `set`. @@ -1540,14 +1901,11 @@ Console.WriteLine(json_src.ToString(4)); There are some extensions built into `STJson` for aggregate operations and simple data processing. Combined with `STJsonPath`, you can do some data manipulation quickly and easily. #O sort -`Sort()` is used to sort the data, internally in a `Merge Sort` manner. This function has the following formal signature: -#TS return|signature|note -STJson|Sort()|Sort without specifying `path`. Default is ascending. -STJson|Sort(bool)|Sort without specifying `path`, specifying whether to sort in descending order. -STJson|Sort(params object[])|Sort without specifying `path` or whether to sort in descending order. -#TE +`Sort()` is used to sort data, internally by `sorting by subsumption`, and `Sort` has several overloads. However, the final version of `Sort` has the following signature, all overloads, and eventually calls this function. +#CS cs +public static STJson Sort(this STJson json, bool is_new_instance, STJsonSortCallback callback); +#CE -Sorting without specifying `path`: #CS cs var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } }; var json_objs = STJson.FromObject(arr_obj); @@ -1556,32 +1914,42 @@ Console.WriteLine(json_objs.Sort(true)); /******************************************************************************* * [output] * *******************************************************************************/ -[true,1,2,4,5,6,{"aa":"aa"},null] -[6,5,4,2,true,1,{"aa":"aa"},null] -#CE -Are you wondering why the result is the way it is? But before that why don't you wonder why you need to use such an array to sort? -Inside `Sort`, a number is obtained for each element and the index of the element is recorded, then the number is sorted and finally an array is reassembled according to the index position to achieve the sorting purpose. The rules for how to get the number of an element are as follows: -#CS cs -switch (item.ValueType) { - case STJsonValueType.Long: - case STJsonValueType.Double: - d_temp = item.GetValue(); - break; - case STJsonValueType.Boolean: // true will convert to 1 - d_temp = item.GetValue() ? 1 : 0; - break; - case STJsonValueType.Datetime: - d_temp = Convert.ToDouble(item.GetValue()); - break; - case STJsonValueType.String: - d_temp = item.Value == null ? 0 : item.Value.ToString().Length; - break; - default: // For other types just let them always be at the end. - d_temp = arr_b_desc[j] ? double.MinValue : double.MaxValue; - break; +[null,1,2,4,5,6,true,{"aa":"aa"}] +[{"aa":"aa"},true,6,5,4,2,1,null] +#CE +Do you wonder why this is the case? But before that why don't you first wonder why you need such an array to sort? As you can see above `Sort` needs a callback function, the `STJson` extension provides two default callback functions which are constructed by the following code: +#CS cs +private static STJsonSortCallback BuildDefaultSortCallback(bool is_desc) +{ + return (a, b) => + { + int n_ret = 0; + if (a.IsNullOrNullValue()) { + n_ret = -1; + } else if (b.IsNullOrNullValue()) { + n_ret = 1; + } else if (a.IsNumber && b.IsNumber) { + n_ret = Convert.ToDouble(a.Value) < Convert.ToDouble(b.Value) ? -1 : 1; + } else if (a.ValueType != b.ValueType) { + n_ret = a.ValueType - b.ValueType; + } else { + switch (a.ValueType) { + case STJsonValueType.Boolean: + n_ret = (bool)b.Value ? -1 : 1; + break; + case STJsonValueType.Datetime: + n_ret = (DateTime)a.Value < (DateTime)b.Value ? -1 : 1; + break; + case STJsonValueType.String: + n_ret = string.Compare(a.Value.ToString(), b.Value.ToString()); + break; + } + } + return is_desc ? -n_ret : n_ret; + }; } #CE -So if you don't have that weird array then everything will be fine. What if I really want to sort only the numbers inside? +So if you don't have that weird array then everything will work fine. What if you really want to just sort the numbers in it? #CS cs var arr_obj = new object[] { 4, 2, 5, 6, 1, true, null, new { aa = "aa" } }; var json_objs = STJson.FromObject(arr_obj) @@ -1596,7 +1964,7 @@ Console.WriteLine(json_objs.Sort(true)); #CE -Specify `path` for sorting: +Sort by `Path`: #CS cs Console.WriteLine(json_src.Sort("age").ToString(4)); /******************************************************************************* @@ -1638,59 +2006,6 @@ Of course you can also specify a descending order: #CS cs Console.WriteLine(json_src.Sort("age", true).ToString(4)); #CE -Multiple fields can also be specified for sorting at the same time: -#CS cs -Console.WriteLine(json_src.Sort("gender", false, "age", true).ToString(4)); -/******************************************************************************* - * [output] * - *******************************************************************************/ -[ - { - "name": "Tony", - "age": 16, - "gender": 0, - "hobby": [ - "game", "dance" - ] - }, { - "name": "Tom", - "age": 16, - "gender": 0, - "hobby": [ - "cooking", "sing" - ] - }, { - "name": "Kun", - "age": 26, - "gender": 1, - "hobby": [ - "sing", "dance", "rap", "basketball" - ] - }, { - "name": "Andy", - "age": 20, - "gender": 1, - "hobby": [ - "draw", "sing" - ] - } -] -#CE -The above is to use `gender` for ascending order, and then for the same value and then `age` for descending order. The `Sort` function parameter rules are as follows: -#CS cs -Sort(string strJsonPath, bool isDesc[, string strJsonPath, bool isDesc]*); -// STLib.Json.STJsonExtension.cs:Sort() -public static STJson Sort(this STJson json, params object[] fields) { - var jsonSort = STJson.CreateArray(); - for (int i = 0; i < fields.Length; i += 2) { - var j = new STJson() - .SetItem("path", fields[i] == null ? null : fields[i].ToString()) - .SetItem("desc", i + 1 < fields.Length ? Convert.ToBoolean(fields[i + 1]) : false); - jsonSort.Append(j); - } - return json.Sort(jsonSort); // internal -} -#CE #O group @@ -1807,7 +2122,7 @@ json_src.Terms("hobby")["hobby"].Count; #CE And is there a possibility that `cardinality` only counts the number of individuals. But.... What if you want to get the values of the de-duplicated fields? And it seems that `Terms()` has already been done in all these cases. It's just not sorting. We already have `Sort()` function, and then a separate order is difficult? #CS cs -json_src.Terms("hobby")["hobby"].Sort("count", true); +json_src.Terms("hobby").ForEach((item)=>item.Sort("count", true)); #CE What do I have to do if I want to get the top three most popular hobbies for personnel? #CS cs @@ -1932,6 +2247,7 @@ Console.WriteLine(json_src.Terms(new STJsonPath("test", "hobby[0]")).ToString(4) #T The END Finally, thank you so damn much for seeing it through to the end, the author is about to go crazy writing it. If you encounter any problems during the use, please inform the author in time if you can. The author will certainly be very promptly recorded, as to change or not ...... We'll talk about it later. + #HS black|white #US TG: DebugST @@ -1939,4 +2255,3 @@ QQ: 2212233137 Mail: 2212233137@qq.com #UE #HE -