From f60736f3c855652c20e3b71301ff26d490d1b86d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 14:41:54 +0200 Subject: [PATCH 01/53] JArray --- System.Linq.Dynamic.Core.sln | 56 +++-- .../ConsoleApp_net6.0.csproj | 2 +- src-console/ConsoleApp_net6.0/Program.cs | 177 +++++++------- .../JObjectExtensions.cs | 217 ++++++++++++++++++ .../JsonExtensions.cs | 66 ++++++ .../JsonParsingConfig.cs | 10 + .../Models/DynamicPropertyWithValue.cs | 11 + ...m.Linq.Dynamic.Core.Newtonsoft.Json.csproj | 34 +++ .../Properties/AssemblyInfo.cs | 1 + 9 files changed, 475 insertions(+), 99 deletions(-) create mode 100644 src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs create mode 100644 src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs create mode 100644 src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index 896175b6..c0a95716 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -141,9 +141,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp_net6.0_EF6_Sqlite", "src-console\ConsoleAppEF6_Sqlite\ConsoleApp_net6.0_EF6_Sqlite.csproj", "{CA03FD55-9DAB-4827-9A35-A96D3804B311}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src-examples", "src-examples", "{BCA2A024-9032-4E56-A6C4-17A15D921728}" - ProjectSection(SolutionItems) = preProject - src-examples\README.md = src-examples\README.md - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeFirst.DataAccess", "src-examples\CodeFirst.DataAccess\CodeFirst.DataAccess.csproj", "{E36D1A08-F3ED-48C7-9DBF-8F625974A6C4}" EndProject @@ -151,6 +148,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeFirst.ConsoleApp", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeFirst.ConsoleApp8", "src-examples\CodeFirst.ConsoleApp8\CodeFirst.ConsoleApp8.csproj", "{68C7FF71-54F6-4D68-B419-65D1B10206D4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Linq.Dynamic.Core.Newtonsoft.Json", "src\System.Linq.Dynamic.Core.Newtonsoft.Json\System.Linq.Dynamic.Core.Newtonsoft.Json.csproj", "{8C5851B8-5C47-4229-AB55-D4252703598E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -869,6 +868,22 @@ Global {51074A4C-15C2-4E72-81F2-2FC53903553B}.Release|x64.Build.0 = Release|Any CPU {51074A4C-15C2-4E72-81F2-2FC53903553B}.Release|x86.ActiveCfg = Release|Any CPU {51074A4C-15C2-4E72-81F2-2FC53903553B}.Release|x86.Build.0 = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|ARM.Build.0 = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x64.ActiveCfg = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x64.Build.0 = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x86.ActiveCfg = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x86.Build.0 = Debug|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|Any CPU.Build.0 = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|ARM.ActiveCfg = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|ARM.Build.0 = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x64.ActiveCfg = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x64.Build.0 = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x86.ActiveCfg = Release|Any CPU + {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x86.Build.0 = Release|Any CPU {E36D1A08-F3ED-48C7-9DBF-8F625974A6C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E36D1A08-F3ED-48C7-9DBF-8F625974A6C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {E36D1A08-F3ED-48C7-9DBF-8F625974A6C4}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -917,22 +932,22 @@ Global {68C7FF71-54F6-4D68-B419-65D1B10206D4}.Release|x64.Build.0 = Release|Any CPU {68C7FF71-54F6-4D68-B419-65D1B10206D4}.Release|x86.ActiveCfg = Release|Any CPU {68C7FF71-54F6-4D68-B419-65D1B10206D4}.Release|x86.Build.0 = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|ARM.ActiveCfg = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|ARM.Build.0 = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x64.ActiveCfg = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x64.Build.0 = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x86.ActiveCfg = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Debug|x86.Build.0 = Debug|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|Any CPU.Build.0 = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|ARM.ActiveCfg = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|ARM.Build.0 = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x64.ActiveCfg = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x64.Build.0 = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x86.ActiveCfg = Release|Any CPU - {CA03FD55-9DAB-4827-9A35-A96D3804B311}.Release|x86.Build.0 = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|ARM.Build.0 = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|x64.ActiveCfg = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|x64.Build.0 = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|x86.ActiveCfg = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Debug|x86.Build.0 = Debug|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|Any CPU.Build.0 = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|ARM.ActiveCfg = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|ARM.Build.0 = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x64.ActiveCfg = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x64.Build.0 = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x86.ActiveCfg = Release|Any CPU + {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -982,10 +997,11 @@ Global {9000129D-322D-4FE6-9C47-75464577C374} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} {ABB1BF71-8927-49BB-99F3-70BCB2CD161E} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} {51074A4C-15C2-4E72-81F2-2FC53903553B} = {122BC4FA-7563-4E35-9D17-077F16F1629F} + {CA03FD55-9DAB-4827-9A35-A96D3804B311} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {E36D1A08-F3ED-48C7-9DBF-8F625974A6C4} = {BCA2A024-9032-4E56-A6C4-17A15D921728} {9E0D0994-7D84-40FF-8383-189F142FEF11} = {BCA2A024-9032-4E56-A6C4-17A15D921728} {68C7FF71-54F6-4D68-B419-65D1B10206D4} = {BCA2A024-9032-4E56-A6C4-17A15D921728} - {CA03FD55-9DAB-4827-9A35-A96D3804B311} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} + {8C5851B8-5C47-4229-AB55-D4252703598E} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj index 84ca9532..49ffafa6 100644 --- a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj +++ b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj @@ -8,7 +8,7 @@ - + diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index 15323cc9..e818eb38 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -2,111 +2,132 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; +using System.Linq.Dynamic.Core.Newtonsoft.Json; using System.Linq.Expressions; +using Newtonsoft.Json.Linq; -namespace ConsoleApp_net6._0 +namespace ConsoleApp_net6._0; + +public class X { - public class X - { - public string Key { get; set; } = null!; + public string Key { get; set; } = null!; - public List? Contestants { get; set; } - } + public List? Contestants { get; set; } +} - public class Y - { - } +public class Y +{ +} - class Program +class Program +{ + static void Main(string[] args) { - static void Main(string[] args) + var array = JArray.Parse(@"[ { - Issue389DoesNotWork(); - return; - Issue389_Works(); - return; - - var q = new[] - { - new X { Key = "x" }, - new X { Key = "a" }, - new X { Key = "a", Contestants = new List { new Y() } } - }.AsQueryable(); - var groupByKey = q.GroupBy("Key"); - var selectQry = groupByKey.Select("new (Key, Sum(np(Contestants.Count, 0)) As TotalCount)").ToDynamicList(); - - Normal(); - Dynamic(); - } + ""first"": 1, + ""City"": ""Paris"", + ""third"": ""test"" + }, + { + ""first"": 2, + ""City"": ""New York"", + ""third"": ""abc"" + }]"); - private static void Issue389_Works() + var results = array.Where("City == @0", "Paris").ToDynamicArray(); + foreach (var result in results) { - var strArray = new[] { "1", "2", "3", "4" }; - var x = new List(); - x.Add(Expression.Parameter(strArray.GetType(), "strArray")); + Console.WriteLine(result.first); + } - string query = "string.Join(\",\", strArray)"; + return; - var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); - Delegate del = e.Compile(); - var result1 = del.DynamicInvoke(new object?[] { strArray }); - Console.WriteLine(result1); - } + Issue389DoesNotWork(); + return; + Issue389_Works(); + return; - private static void Issue389WorksWithInts() + var q = new[] { - var intArray = new object[] { 1, 2, 3, 4 }; - var x = new List(); - x.Add(Expression.Parameter(intArray.GetType(), "intArray")); + new X { Key = "x" }, + new X { Key = "a" }, + new X { Key = "a", Contestants = new List { new Y() } } + }.AsQueryable(); + var groupByKey = q.GroupBy("Key"); + var selectQry = groupByKey.Select("new (Key, Sum(np(Contestants.Count, 0)) As TotalCount)").ToDynamicList(); + + Normal(); + Dynamic(); + } - string query = "string.Join(\",\", intArray)"; + private static void Issue389_Works() + { + var strArray = new[] { "1", "2", "3", "4" }; + var x = new List(); + x.Add(Expression.Parameter(strArray.GetType(), "strArray")); - var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); - Delegate del = e.Compile(); - var result = del.DynamicInvoke(new object?[] { intArray }); + string query = "string.Join(\",\", strArray)"; - Console.WriteLine(result); - } + var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); + Delegate del = e.Compile(); + var result1 = del.DynamicInvoke(new object?[] { strArray }); + Console.WriteLine(result1); + } - private static void Issue389DoesNotWork() - { - var intArray = new [] { 1, 2, 3, 4 }; - var x = new List(); - x.Add(Expression.Parameter(intArray.GetType(), "intArray")); + private static void Issue389WorksWithInts() + { + var intArray = new object[] { 1, 2, 3, 4 }; + var x = new List(); + x.Add(Expression.Parameter(intArray.GetType(), "intArray")); - string query = "string.Join(\",\", intArray)"; + string query = "string.Join(\",\", intArray)"; - var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); - Delegate del = e.Compile(); - var result = del.DynamicInvoke(new object?[] { intArray }); + var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); + Delegate del = e.Compile(); + var result = del.DynamicInvoke(new object?[] { intArray }); - Console.WriteLine(result); - } + Console.WriteLine(result); + } - private static void Normal() - { - var e = new int[0].AsQueryable(); - var q = new[] { 1 }.AsQueryable(); + private static void Issue389DoesNotWork() + { + var intArray = new[] { 1, 2, 3, 4 }; + var x = new List(); + x.Add(Expression.Parameter(intArray.GetType(), "intArray")); - var a = q.FirstOrDefault(); - var b = e.FirstOrDefault(44); + string query = "string.Join(\",\", intArray)"; - var c = q.FirstOrDefault(i => i == 0); - var d = q.FirstOrDefault(i => i == 0, 42); + var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); + Delegate del = e.Compile(); + var result = del.DynamicInvoke(new object?[] { intArray }); - var t = q.Take(1); - } + Console.WriteLine(result); + } - private static void Dynamic() - { - var e = new int[0].AsQueryable() as IQueryable; - var q = new[] { 1 }.AsQueryable() as IQueryable; + private static void Normal() + { + var e = new int[0].AsQueryable(); + var q = new[] { 1 }.AsQueryable(); - var a = q.FirstOrDefault(); - //var b = e.FirstOrDefault(44); + var a = q.FirstOrDefault(); + var b = e.FirstOrDefault(44); - var c = q.FirstOrDefault("it == 0"); - //var d = q.FirstOrDefault(i => i == 0, 42); - } + var c = q.FirstOrDefault(i => i == 0); + var d = q.FirstOrDefault(i => i == 0, 42); + + var t = q.Take(1); + } + + private static void Dynamic() + { + var e = new int[0].AsQueryable() as IQueryable; + var q = new[] { 1 }.AsQueryable() as IQueryable; + + var a = q.FirstOrDefault(); + //var b = e.FirstOrDefault(44); + + var c = q.FirstOrDefault("it == 0"); + //var d = q.FirstOrDefault(i => i == 0, 42); } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs new file mode 100644 index 00000000..2f866294 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs @@ -0,0 +1,217 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq.Dynamic.Core.Newtonsoft.Json.Models; +using System.Reflection; +using JsonConverter.Abstractions.Models; +using Newtonsoft.Json.Linq; + +namespace System.Linq.Dynamic.Core.Newtonsoft.Json; + +/// +/// Based on https://github.com/StefH/JsonConverter/blob/main/src/JsonConverter.Newtonsoft.Json/Extensions/JObjectExtensions.cs +/// +internal static class JObjectExtensions +{ + private class JTokenResolvers : Dictionary> + { + } + + private static readonly JTokenResolvers Resolvers = new() + { + { JTokenType.Array, ConvertJTokenArray }, + { JTokenType.Boolean, (jToken, _) => jToken.Value() }, + { JTokenType.Bytes, (jToken, _) => jToken.Value() }, + { JTokenType.Date, (jToken, _) => jToken.Value() }, + { JTokenType.Float, ConvertJTokenFloat }, + { JTokenType.Guid, (jToken, _) => jToken.Value() }, + { JTokenType.Integer, ConvertJTokenInteger }, + { JTokenType.None, (_, _) => null }, + { JTokenType.Null, (_, _) => null }, + { JTokenType.Object, ConvertJObject }, + { JTokenType.Property, ConvertJTokenProperty }, + { JTokenType.String, (jToken, _) => jToken.Value() }, + { JTokenType.TimeSpan, (jToken, _) => jToken.Value() }, + { JTokenType.Undefined, (_, _) => null }, + { JTokenType.Uri, (o, _) => o.Value() }, + }; + + internal static object? ToDynamicClass(this JValue src) + { + return src.Value; + } + + internal static DynamicClass? ToDynamicClass(this JObject? src, DynamicJsonClassOptions? options = null) + { + if (src == null) + { + return null; + } + + var dynamicPropertyWithValues = new List(); + + foreach (var prop in src.Properties()) + { + var value = Resolvers[prop.Type](prop.Value, options); + if (value != null) + { + dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value)); + } + } + + return CreateInstance(dynamicPropertyWithValues); + } + + internal static IEnumerable ToDynamicJsonClassArray(this JArray? src, DynamicJsonClassOptions? options = null) + { + if (src == null) + { + return new object?[0]; + } + + return ConvertJTokenArray(src, options); + } + + internal static object? ToDynamicClass(this JToken? src, DynamicJsonClassOptions? options = null) + { + if (src == null) + { + return null; + } + + return GetResolverFor(src)(src, options); + } + + private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg is JObject asJObject) + { + return asJObject.ToDynamicClass(options); + } + + return GetResolverFor(arg)(arg, options); + } + + private static object PassThrough(JToken arg, DynamicJsonClassOptions? options) + { + return arg; + } + + private static Func GetResolverFor(JToken arg) + { + return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough; + } + + private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg.Type != JTokenType.Float) + { + throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float."); + } + + if (options?.FloatConvertBehavior == FloatBehavior.UseFloat) + { + try + { + return arg.Value(); + } + catch + { + return arg.Value(); + } + } + + if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal) + { + try + { + return arg.Value(); + } + catch + { + return arg.Value(); + } + } + + + return arg.Value(); + } + + private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg.Type != JTokenType.Integer) + { + throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int."); + } + + var longValue = arg.Value(); + + if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt) + { + if (longValue is >= int.MinValue and <= int.MaxValue) + { + return Convert.ToInt32(longValue); + } + } + + return longValue; + } + + private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null) + { + var resolver = GetResolverFor(arg); + if (resolver is null) + { + throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}."); + } + + return resolver(arg, options); + } + + private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg is not JArray array) + { + throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}."); + } + + var result = new List(); + foreach (var item in array) + { + result.Add(ConvertJObject(item)); + } + + var distinctType = FindSameTypeOf(result); + return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType); + } + + private static Type? FindSameTypeOf(IEnumerable src) + { + var types = src.Select(o => o?.GetType()).Distinct().OfType().ToArray(); + return types.Length == 1 ? types[0] : null; + } + + private static IEnumerable ConvertToTypedArray(IEnumerable src, Type newType) + { + var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType); + return (IEnumerable)method.Invoke(null, new object[] { src })!; + } + + private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JObjectExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!; + + private static T[] ConvertToTypedArrayGeneric(IEnumerable src) + { + return src.Cast().ToArray(); + } + + private static DynamicClass CreateInstance(IList dynamicPropertiesWithValue) + { + var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray()); + var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; + foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) + { + dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); + } + + return dynamicClass; + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs new file mode 100644 index 00000000..b02a5d49 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs @@ -0,0 +1,66 @@ +using System.Linq.Dynamic.Core.Validation; +using Newtonsoft.Json.Linq; + +namespace System.Linq.Dynamic.Core.Newtonsoft.Json; + +public static class JsonExtensions +{ + public static JArray Where(this JArray source, JsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNullOrEmpty(predicate); + + var array = new JArray(); + + if (source.Count == 0) + { + return array; + } + + // Convert the JArray to a dynamic object array / queryable. + var enumerable = source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); + + // Apply the where clause. + var results = enumerable.Where(config, predicate, args); + + // Convert the dynamic results back to a JArray. + foreach (var result in results) + { + array.Add(JObject.FromObject(result)); + } + + return array; + } + + public static JArray Where(this JArray source, string predicate, params object?[] args) + { + return Where(source, JsonParsingConfig.Default, predicate, args); + } + + //private static object? ConvertToDynamicClass(object value, DynamicJsonClassOptions? options = null) + //{ + // Check.NotNull(value); + + // if (value is JObject jObject) + // { + // return jObject.ToDynamicClass(options); + // } + + // if (value is JArray jArray) + // { + // return jArray.ToDynamicJsonClassArray(options); + // } + + // if (value is JValue jValue) + // { + // return jValue.ToDynamicClass(options); + // } + + // if (value is JToken jToken) + // { + // return jToken.ToDynamicClass(options); + // } + + // return value; + //} +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs new file mode 100644 index 00000000..8470bf27 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs @@ -0,0 +1,10 @@ +using JsonConverter.Abstractions.Models; + +namespace System.Linq.Dynamic.Core.Newtonsoft.Json; + +public class JsonParsingConfig : ParsingConfig +{ + public static JsonParsingConfig Default { get; } = new(); + + public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs new file mode 100644 index 00000000..e0c8be25 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs @@ -0,0 +1,11 @@ +namespace System.Linq.Dynamic.Core.Newtonsoft.Json.Models; + +internal class DynamicPropertyWithValue : DynamicProperty +{ + public object? Value { get; } + + public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object)) + { + Value = value; + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj new file mode 100644 index 00000000..60f0aefb --- /dev/null +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj @@ -0,0 +1,34 @@ + + + + + + ../System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk + System.Linq.Dynamic.Core + Stef Heyenrath + TODO + system;linq;dynamic;core;dotnet;json + {8C5851B8-5C47-4229-AB55-D4252703598E} + net35;net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 + 1.3.$(PatchVersion) + + + + full + + + + portable + true + + + + + + + + + + + + \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs index 255df480..6e054f68 100644 --- a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs +++ b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Newtonsoft.Json, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("EntityFramework.DynamicLinq.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("EntityFramework.DynamicLinq.Tests.net452, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests.Net6, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] From 7f4f273b8a62efef344936133499c6b5aa0ed8d9 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 15:11:38 +0200 Subject: [PATCH 02/53] ... --- .../JsonExtensions.cs | 119 ++++++++++++++++-- ...m.Linq.Dynamic.Core.Newtonsoft.Json.csproj | 1 - 2 files changed, 106 insertions(+), 14 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs index b02a5d49..a664360f 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs @@ -3,38 +3,131 @@ namespace System.Linq.Dynamic.Core.Newtonsoft.Json; +/// +/// +/// public static class JsonExtensions { - public static JArray Where(this JArray source, JsonParsingConfig config, string predicate, params object?[] args) + #region All + /// Determines whether all the elements of a sequence satisfy a condition. + /// A sequence whose elements to test for a condition. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. + public static bool All(this JArray source, string predicate, params object?[] args) + { + return All(source, JsonParsingConfig.Default, predicate, args); + } + + /// Determines whether all the elements of a sequence satisfy a condition. + /// A sequence whose elements to test for a condition. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. + public static bool All(this JArray source, JsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); - Check.NotNullOrEmpty(predicate); + Check.NotNull(config); + Check.NotEmpty(predicate); - var array = new JArray(); + var queryable = ConvertToQueryable(source, config); + return queryable.All(config, predicate, args); + } + #endregion All + + #region Select + /// + /// Projects each element of a sequence into a new form. + /// + /// A sequence of values to project. + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JArray Select(this JArray source, string selector, params object?[] args) + { + return Select(source, JsonParsingConfig.Default, selector, args); + } + + /// + /// Projects each element of a sequence into a new form. + /// + /// A sequence of values to project. + /// The . + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JArray Select(this JArray source, JsonParsingConfig config, string selector, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotNullOrEmpty(selector); if (source.Count == 0) { - return array; + return new JArray(); } - // Convert the JArray to a dynamic object array / queryable. - var enumerable = source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); + var queryable = ConvertToQueryable(source, config); + return ToJArray(() => queryable.Select(config, selector, args)); + } + #endregion Select + + #region Where + /// + /// Filters a sequence of values based on a predicate. + /// + /// A to filter. + /// An expression string to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A that contains elements from the input sequence that satisfy the condition specified by predicate. + public static JArray Where(this JArray source, string predicate, params object?[] args) + { + return Where(source, JsonParsingConfig.Default, predicate, args); + } - // Apply the where clause. - var results = enumerable.Where(config, predicate, args); + /// + /// Filters a sequence of values based on a predicate. + /// + /// A to filter. + /// The . + /// An expression string to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A that contains elements from the input sequence that satisfy the condition specified by predicate. + public static JArray Where(this JArray source, JsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotNullOrEmpty(predicate); - // Convert the dynamic results back to a JArray. - foreach (var result in results) + if (source.Count == 0) { - array.Add(JObject.FromObject(result)); + return new JArray(); } + var queryable = ConvertToQueryable(source, config); + return ToJArray(() => queryable.Where(config, predicate, args)); + } + #endregion Where + + /// + /// Convert the dynamic results to a JArray. + /// + /// The callback which returns a . + /// + private static JArray ToJArray(Func func) + { + var array = new JArray(); + foreach (var element in func()) + { + array.Add(JObject.FromObject(element)); + } return array; } - public static JArray Where(this JArray source, string predicate, params object?[] args) + private static IQueryable ConvertToQueryable(JArray source, JsonParsingConfig config) { - return Where(source, JsonParsingConfig.Default, predicate, args); + return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); } //private static object? ConvertToDynamicClass(object value, DynamicJsonClassOptions? options = null) diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj index 60f0aefb..f36d7f0f 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj @@ -2,7 +2,6 @@ - ../System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk System.Linq.Dynamic.Core Stef Heyenrath From 5ea2d446bdd2889cb74fe4ac9868ed786db0f5d9 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 15:38:29 +0200 Subject: [PATCH 03/53] Select --- src-console/ConsoleApp_net6.0/Program.cs | 12 +++++++++--- .../JsonExtensions.cs | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index e818eb38..19f7f50b 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -35,10 +35,16 @@ static void Main(string[] args) ""third"": ""abc"" }]"); - var results = array.Where("City == @0", "Paris").ToDynamicArray(); - foreach (var result in results) + var where = array.Where("City == @0", "Paris"); + foreach (var result in where) { - Console.WriteLine(result.first); + Console.WriteLine(result["first"]); + } + + var select = array.Select("City"); + foreach (var result in select) + { + Console.WriteLine(result); } return; diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs index a664360f..405cfbb9 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs @@ -118,9 +118,10 @@ public static JArray Where(this JArray source, JsonParsingConfig config, string private static JArray ToJArray(Func func) { var array = new JArray(); - foreach (var element in func()) + foreach (var dynamicElement in func()) { - array.Add(JObject.FromObject(element)); + var element = dynamicElement is DynamicClass dynamicClass ? JObject.FromObject(dynamicClass) : dynamicElement; + array.Add(element); } return array; } From 4a97bee013df1faa7896f7410ff8b6dadb6c4579 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 15:39:50 +0200 Subject: [PATCH 04/53] . --- src-console/ConsoleApp_net6.0/Program.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index 19f7f50b..329acdb9 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -47,6 +47,12 @@ static void Main(string[] args) Console.WriteLine(result); } + var whereWithSelect = array.Where("City == @0", "Paris").Select("first"); + foreach (var result in whereWithSelect) + { + Console.WriteLine(result); + } + return; Issue389DoesNotWork(); From 33a5ecc5e481fb4fae4f7695447a8091d36637a2 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 18:58:47 +0200 Subject: [PATCH 05/53] tests --- System.Linq.Dynamic.Core.sln | 21 ++++++++- .../ConsoleApp_net6.0.csproj | 2 +- src-console/ConsoleApp_net6.0/Program.cs | 2 +- .../Config}/JsonParsingConfig.cs | 2 +- .../Extensions}/JObjectExtensions.cs | 4 +- .../JsonExtensions.cs | 6 ++- .../Models/DynamicPropertyWithValue.cs | 2 +- ...m.Linq.Dynamic.Core.NewtonsoftJson.csproj} | 3 +- .../Properties/AssemblyInfo.cs | 2 +- .../NewtonsoftJsonTests.cs | 34 ++++++++++++++ ...q.Dynamic.Core.NewtonsoftJson.Tests.csproj | 45 +++++++++++++++++++ .../Json/NewtonsoftJsonTests.cs | 34 ++++++++++++++ 12 files changed, 145 insertions(+), 12 deletions(-) rename src/{System.Linq.Dynamic.Core.Newtonsoft.Json => System.Linq.Dynamic.Core.NewtonsoftJson/Config}/JsonParsingConfig.cs (80%) rename src/{System.Linq.Dynamic.Core.Newtonsoft.Json => System.Linq.Dynamic.Core.NewtonsoftJson/Extensions}/JObjectExtensions.cs (98%) rename src/{System.Linq.Dynamic.Core.Newtonsoft.Json => System.Linq.Dynamic.Core.NewtonsoftJson}/JsonExtensions.cs (97%) rename src/{System.Linq.Dynamic.Core.Newtonsoft.Json => System.Linq.Dynamic.Core.NewtonsoftJson}/Models/DynamicPropertyWithValue.cs (79%) rename src/{System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj => System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj} (88%) create mode 100644 test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs create mode 100644 test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj create mode 100644 test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index c0a95716..ecad734d 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -148,7 +148,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeFirst.ConsoleApp", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeFirst.ConsoleApp8", "src-examples\CodeFirst.ConsoleApp8\CodeFirst.ConsoleApp8.csproj", "{68C7FF71-54F6-4D68-B419-65D1B10206D4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Linq.Dynamic.Core.Newtonsoft.Json", "src\System.Linq.Dynamic.Core.Newtonsoft.Json\System.Linq.Dynamic.Core.Newtonsoft.Json.csproj", "{8C5851B8-5C47-4229-AB55-D4252703598E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.NewtonsoftJson", "src\System.Linq.Dynamic.Core.NewtonsoftJson\System.Linq.Dynamic.Core.NewtonsoftJson.csproj", "{8C5851B8-5C47-4229-AB55-D4252703598E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.NewtonsoftJson.Tests", "test\System.Linq.Dynamic.Core.NewtonsoftJson.Tests\System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj", "{912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -948,6 +950,22 @@ Global {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x64.Build.0 = Release|Any CPU {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x86.ActiveCfg = Release|Any CPU {8C5851B8-5C47-4229-AB55-D4252703598E}.Release|x86.Build.0 = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|ARM.ActiveCfg = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|ARM.Build.0 = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|x64.ActiveCfg = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|x64.Build.0 = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|x86.ActiveCfg = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Debug|x86.Build.0 = Debug|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|Any CPU.Build.0 = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|ARM.ActiveCfg = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|ARM.Build.0 = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x64.ActiveCfg = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x64.Build.0 = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x86.ActiveCfg = Release|Any CPU + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1002,6 +1020,7 @@ Global {9E0D0994-7D84-40FF-8383-189F142FEF11} = {BCA2A024-9032-4E56-A6C4-17A15D921728} {68C7FF71-54F6-4D68-B419-65D1B10206D4} = {BCA2A024-9032-4E56-A6C4-17A15D921728} {8C5851B8-5C47-4229-AB55-D4252703598E} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj index 49ffafa6..4718945a 100644 --- a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj +++ b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj @@ -8,7 +8,7 @@ - + diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index 329acdb9..9a60a639 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; -using System.Linq.Dynamic.Core.Newtonsoft.Json; +using System.Linq.Dynamic.Core.NewtonsoftJson; using System.Linq.Expressions; using Newtonsoft.Json.Linq; diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/JsonParsingConfig.cs similarity index 80% rename from src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/JsonParsingConfig.cs index 8470bf27..3da81b59 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/JsonParsingConfig.cs @@ -1,6 +1,6 @@ using JsonConverter.Abstractions.Models; -namespace System.Linq.Dynamic.Core.Newtonsoft.Json; +namespace System.Linq.Dynamic.Core.NewtonsoftJson.Config; public class JsonParsingConfig : ParsingConfig { diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs similarity index 98% rename from src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs index 2f866294..1469fbdd 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JObjectExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs @@ -1,11 +1,11 @@ using System.Collections; using System.Collections.Generic; -using System.Linq.Dynamic.Core.Newtonsoft.Json.Models; +using System.Linq.Dynamic.Core.NewtonsoftJson.Models; using System.Reflection; using JsonConverter.Abstractions.Models; using Newtonsoft.Json.Linq; -namespace System.Linq.Dynamic.Core.Newtonsoft.Json; +namespace System.Linq.Dynamic.Core.NewtonsoftJson.Extensions; /// /// Based on https://github.com/StefH/JsonConverter/blob/main/src/JsonConverter.Newtonsoft.Json/Extensions/JObjectExtensions.cs diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs similarity index 97% rename from src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs index 405cfbb9..c7a4e1f0 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs @@ -1,7 +1,9 @@ -using System.Linq.Dynamic.Core.Validation; +using System.Linq.Dynamic.Core.NewtonsoftJson.Config; +using System.Linq.Dynamic.Core.NewtonsoftJson.Extensions; +using System.Linq.Dynamic.Core.Validation; using Newtonsoft.Json.Linq; -namespace System.Linq.Dynamic.Core.Newtonsoft.Json; +namespace System.Linq.Dynamic.Core.NewtonsoftJson; /// /// diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs similarity index 79% rename from src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs index e0c8be25..3734ec58 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/Models/DynamicPropertyWithValue.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs @@ -1,4 +1,4 @@ -namespace System.Linq.Dynamic.Core.Newtonsoft.Json.Models; +namespace System.Linq.Dynamic.Core.NewtonsoftJson.Models; internal class DynamicPropertyWithValue : DynamicProperty { diff --git a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj similarity index 88% rename from src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj index f36d7f0f..39673a59 100644 --- a/src/System.Linq.Dynamic.Core.Newtonsoft.Json/System.Linq.Dynamic.Core.Newtonsoft.Json.csproj +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj @@ -22,8 +22,7 @@ - - + diff --git a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs index 6e054f68..5d862943 100644 --- a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs +++ b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Newtonsoft.Json, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] +[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.NewtonsoftJson, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("EntityFramework.DynamicLinq.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("EntityFramework.DynamicLinq.Tests.net452, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests.Net6, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs new file mode 100644 index 00000000..17e9347f --- /dev/null +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -0,0 +1,34 @@ +using System.Linq; +using System.Linq.Dynamic.Core.NewtonsoftJson; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests.Json; + +public class NewtonsoftJsonTests +{ + [Fact] + public void All() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var jArray = JArray.Parse(json); + + // Act + var result = jArray.All("Age > 20"); + + // Assert + result.Should().BeTrue(); + } +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj new file mode 100644 index 00000000..07df4d23 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj @@ -0,0 +1,45 @@ + + + Stef Heyenrath + net452;netcoreapp3.1;net8.0 + + full + True + latest + enable + ../../src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs new file mode 100644 index 00000000..17e9347f --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs @@ -0,0 +1,34 @@ +using System.Linq; +using System.Linq.Dynamic.Core.NewtonsoftJson; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests.Json; + +public class NewtonsoftJsonTests +{ + [Fact] + public void All() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var jArray = JArray.Parse(json); + + // Act + var result = jArray.All("Age > 20"); + + // Assert + result.Should().BeTrue(); + } +} \ No newline at end of file From 2761151e8622f1a60ad7c540d3dc1a4fdf9eb91b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 19:48:04 +0200 Subject: [PATCH 06/53] fix tst --- .../Json/NewtonsoftJsonTests.cs | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs diff --git a/test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs deleted file mode 100644 index 17e9347f..00000000 --- a/test/System.Linq.Dynamic.Core.Tests/Json/NewtonsoftJsonTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Linq; -using System.Linq.Dynamic.Core.NewtonsoftJson; -using FluentAssertions; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace System.Linq.Dynamic.Core.Tests.Json; - -public class NewtonsoftJsonTests -{ - [Fact] - public void All() - { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var jArray = JArray.Parse(json); - - // Act - var result = jArray.All("Age > 20"); - - // Assert - result.Should().BeTrue(); - } -} \ No newline at end of file From db3fabed4f1f6a4f4b56840bb0fe1e0f4dc03ffd Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 19:54:49 +0200 Subject: [PATCH 07/53] CI --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c00b9b2d..a4c26683 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: - name: Build Projects run: | dotnet build ./src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj -c Release -p:buildType=azure-pipelines-ci + dotnet build ./src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj -c Release -p:buildType=azure-pipelines-ci - name: Run Tests net8.0 run: | @@ -42,3 +43,8 @@ jobs: run: | dotnet build ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj -c Release -f net452 -p:buildType=azure-pipelines-ci dotnet test ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj -c Release -f net452 -p:buildType=azure-pipelines-ci --no-build + + - name: Run Json Tests .NET 8 + run: | + dotnet build ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci + dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci --no-build From ef538b8b3ef2ca30c992f77516904fef839e9ebc Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 20:16:53 +0200 Subject: [PATCH 08/53] net35; --- System.Linq.Dynamic.Core.sln | 19 +++ .../Extensions/JObjectExtensions.cs | 14 +- .../JsonExtensions.cs | 30 ---- ...em.Linq.Dynamic.Core.NewtonsoftJson.csproj | 2 +- .../Config/JsonParsingConfig.cs | 10 ++ .../Extensions/JsonDocumentExtensions.cs | 15 ++ .../Extensions/JsonElementExtensions.cs | 15 ++ .../JsonExtensions.cs | 134 ++++++++++++++++++ .../Models/DynamicPropertyWithValue.cs | 11 ++ ...em.Linq.Dynamic.Core.SystemTextJson.csproj | 33 +++++ .../Properties/AssemblyInfo.cs | 3 +- .../NewtonsoftJsonTests.cs | 6 +- ...q.Dynamic.Core.SystemTextJson.Tests.csproj | 45 ++++++ .../SystemTextJsonTests.cs | 34 +++++ 14 files changed, 323 insertions(+), 48 deletions(-) create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj create mode 100644 test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj create mode 100644 test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index ecad734d..e0f3907d 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -152,6 +152,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.Ne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.NewtonsoftJson.Tests", "test\System.Linq.Dynamic.Core.NewtonsoftJson.Tests\System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj", "{912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.SystemTextJson", "src\System.Linq.Dynamic.Core.SystemTextJson\System.Linq.Dynamic.Core.SystemTextJson.csproj", "{FA01CE15-315A-499E-AFC2-955CA7EB45FF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -966,6 +968,22 @@ Global {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x64.Build.0 = Release|Any CPU {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x86.ActiveCfg = Release|Any CPU {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90}.Release|x86.Build.0 = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|ARM.Build.0 = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|x64.Build.0 = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Debug|x86.Build.0 = Debug|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|Any CPU.Build.0 = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|ARM.ActiveCfg = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|ARM.Build.0 = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x64.ActiveCfg = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x64.Build.0 = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x86.ActiveCfg = Release|Any CPU + {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1021,6 +1039,7 @@ Global {68C7FF71-54F6-4D68-B419-65D1B10206D4} = {BCA2A024-9032-4E56-A6C4-17A15D921728} {8C5851B8-5C47-4229-AB55-D4252703598E} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} + {FA01CE15-315A-499E-AFC2-955CA7EB45FF} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs index 1469fbdd..db90dcbf 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs @@ -63,22 +63,12 @@ private class JTokenResolvers : Dictionary -/// -/// public static class JsonExtensions { #region All @@ -132,31 +129,4 @@ private static IQueryable ConvertToQueryable(JArray source, JsonParsingConfig co { return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); } - - //private static object? ConvertToDynamicClass(object value, DynamicJsonClassOptions? options = null) - //{ - // Check.NotNull(value); - - // if (value is JObject jObject) - // { - // return jObject.ToDynamicClass(options); - // } - - // if (value is JArray jArray) - // { - // return jArray.ToDynamicJsonClassArray(options); - // } - - // if (value is JValue jValue) - // { - // return jValue.ToDynamicClass(options); - // } - - // if (value is JToken jToken) - // { - // return jToken.ToDynamicClass(options); - // } - - // return value; - //} } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj index 39673a59..24d87076 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj @@ -8,7 +8,7 @@ TODO system;linq;dynamic;core;dotnet;json {8C5851B8-5C47-4229-AB55-D4252703598E} - net35;net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 + net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 1.3.$(PatchVersion) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs new file mode 100644 index 00000000..49e21f82 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs @@ -0,0 +1,10 @@ +using JsonConverter.Abstractions.Models; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Config; + +public class JsonParsingConfig : ParsingConfig +{ + public static JsonParsingConfig Default { get; } = new(); + + public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs new file mode 100644 index 00000000..777261c8 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; + +/// +/// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonDocumentExtensions.cs +/// +internal static class JsonDocumentExtensions +{ + public static T? ToObject(this JsonDocument document, JsonSerializerOptions options) + { + var rawText = document.RootElement.GetRawText(); + return JsonSerializer.Deserialize(rawText, options); + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs new file mode 100644 index 00000000..9394d90b --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; + +/// +/// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonElementExtensions.cs +/// +internal static class JsonElementExtensions +{ + public static T? ToObject(this JsonElement element, JsonSerializerOptions options) + { + var rawText = element.GetRawText(); + return JsonSerializer.Deserialize(rawText, options); + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs new file mode 100644 index 00000000..2dc05e11 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs @@ -0,0 +1,134 @@ +using System.Linq.Dynamic.Core.SystemTextJson.Config; +using System.Linq.Dynamic.Core.Validation; +using System.Text.Json; + +namespace System.Linq.Dynamic.Core.SystemTextJson; + +/// +/// +/// +public static class JsonExtensions +{ + #region All + /// Determines whether all the elements of a sequence satisfy a condition. + /// A sequence whose elements to test for a condition. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. + public static bool All(this JsonDocument source, string predicate, params object?[] args) + { + return All(source, JsonParsingConfig.Default, predicate, args); + } + + /// Determines whether all the elements of a sequence satisfy a condition. + /// A sequence whose elements to test for a condition. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. + public static bool All(this JsonDocument source, JsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotEmpty(predicate); + + var queryable = ConvertToQueryable(source, config); + return queryable.All(config, predicate, args); + } + #endregion All + + #region Select + /// + /// Projects each element of a sequence into a new form. + /// + /// A sequence of values to project. + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JsonDocument Select(this JsonDocument source, string selector, params object?[] args) + { + return Select(source, JsonParsingConfig.Default, selector, args); + } + + /// + /// Projects each element of a sequence into a new form. + /// + /// A sequence of values to project. + /// The . + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JsonDocument Select(this JsonDocument source, JsonParsingConfig config, string selector, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotNullOrEmpty(selector); + + if (source.Count == 0) + { + return new JArray(); + } + + var queryable = ConvertToQueryable(source, config); + return ToJArray(() => queryable.Select(config, selector, args)); + } + #endregion Select + + #region Where + /// + /// Filters a sequence of values based on a predicate. + /// + /// A to filter. + /// An expression string to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A that contains elements from the input sequence that satisfy the condition specified by predicate. + public static JsonDocument Where(this JsonDocument source, string predicate, params object?[] args) + { + return Where(source, JsonParsingConfig.Default, predicate, args); + } + + /// + /// Filters a sequence of values based on a predicate. + /// + /// A to filter. + /// The . + /// An expression string to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A that contains elements from the input sequence that satisfy the condition specified by predicate. + public static JsonDocument Where(this JsonDocument source, JsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotNullOrEmpty(predicate); + + if (source.Count == 0) + { + return new JArray(); + } + + var queryable = ConvertToQueryable(source, config); + return ToJArray(() => queryable.Where(config, predicate, args)); + } + #endregion Where + + /// + /// Convert the dynamic results to a JArray. + /// + /// The callback which returns a . + /// + private static JsonDocument ToJArray(Func func) + { + var array = new JsonDocument(); + foreach (var dynamicElement in func()) + { + var element = dynamicElement is DynamicClass dynamicClass ? JObject.FromObject(dynamicClass) : dynamicElement; + array.Add(element); + } + return array; + } + + private static IQueryable ConvertToQueryable(JsonDocument source, JsonParsingConfig config) + { + return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs new file mode 100644 index 00000000..806703f3 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs @@ -0,0 +1,11 @@ +namespace System.Linq.Dynamic.Core.SystemTextJson.Models; + +internal class DynamicPropertyWithValue : DynamicProperty +{ + public object? Value { get; } + + public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object)) + { + Value = value; + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj new file mode 100644 index 00000000..80a9345e --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -0,0 +1,33 @@ + + + + + ../System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk + System.Linq.Dynamic.Core + Stef Heyenrath + TODO + system;linq;dynamic;core;dotnet;json + {FA01CE15-315A-499E-AFC2-955CA7EB45FF} + netstandard2.0;netstandard2.1;net6.0;net8.0 + 1.3.$(PatchVersion) + fa8622ae-b712-4c03-8963-0d24e0dba084 + + + + full + + + + portable + true + + + + + + + + + + + \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs index 5d862943..6f920ac5 100644 --- a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs +++ b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs @@ -1,9 +1,10 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.SystemTextJson, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.NewtonsoftJson, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("EntityFramework.DynamicLinq.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("EntityFramework.DynamicLinq.Tests.net452, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests.Net6, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests.Net5, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] [assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 17e9347f..d1340822 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -1,10 +1,8 @@ -using System.Linq; -using System.Linq.Dynamic.Core.NewtonsoftJson; -using FluentAssertions; +using FluentAssertions; using Newtonsoft.Json.Linq; using Xunit; -namespace System.Linq.Dynamic.Core.Tests.Json; +namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests; public class NewtonsoftJsonTests { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj new file mode 100644 index 00000000..07df4d23 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj @@ -0,0 +1,45 @@ + + + Stef Heyenrath + net452;netcoreapp3.1;net8.0 + + full + True + latest + enable + ../../src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs new file mode 100644 index 00000000..17e9347f --- /dev/null +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -0,0 +1,34 @@ +using System.Linq; +using System.Linq.Dynamic.Core.NewtonsoftJson; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests.Json; + +public class NewtonsoftJsonTests +{ + [Fact] + public void All() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var jArray = JArray.Parse(json); + + // Act + var result = jArray.All("Age > 20"); + + // Assert + result.Should().BeTrue(); + } +} \ No newline at end of file From d0875b9bd40f366d5b48c4e4d0c3acff06ef5299 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 22:04:01 +0200 Subject: [PATCH 09/53] --- --- .../Extensions/JObjectExtensions.cs | 208 ++++++++++++++++++ .../Extensions/JsonDocumentExtensions.cs | 2 +- 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs new file mode 100644 index 00000000..47ed5863 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs @@ -0,0 +1,208 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq.Dynamic.Core.SystemTextJson.Models; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Nodes; +using JsonConverter.Abstractions.Models; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; + +/// +/// Based on https://github.com/StefH/JsonConverter/blob/main/src/JsonConverter.Newtonsoft.Json/Extensions/JObjectExtensions.cs +/// +internal static class JsonDocumentExtensions +{ + private class JTokenResolvers : Dictionary> + { + } + + private static readonly JTokenResolvers Resolvers = new() + { + { JsonValueKind.Array, ConvertJTokenArray }, + { JsonValueKind.Boolean, (jToken, _) => jToken.Value() }, + { JsonValueKind.Bytes, (jToken, _) => jToken.Value() }, + { JsonValueKind.Date, (jToken, _) => jToken.Value() }, + { JsonValueKind.Float, ConvertJTokenFloat }, + { JsonValueKind.Guid, (jToken, _) => jToken.Value() }, + { JsonValueKind.Integer, ConvertJTokenInteger }, + { JsonValueKind.None, (_, _) => null }, + { JsonValueKind.Null, (_, _) => null }, + { JsonValueKind.Object, ConvertJObject }, + { JsonValueKind.Property, ConvertJTokenProperty }, + { JsonValueKind.String, (jToken, _) => jToken.Value() }, + { JsonValueKind.TimeSpan, (jToken, _) => jToken.Value() }, + { JsonValueKind.Undefined, (_, _) => null }, + { JsonValueKind.Uri, (o, _) => o.Value() }, + }; + + //internal static object? ToDynamicClass(this JValue src) + //{ + // return src.Value; + //} + + internal static DynamicClass? ToDynamicClass(this JsonDocument? src, DynamicJsonClassOptions? options = null) + { + if (src == null) + { + return null; + } + + var dynamicPropertyWithValues = new List(); + + foreach (var prop in src.RootElement.EnumerateObject()) + { + var value = Resolvers[prop.Value.ValueKind](prop.Value, options); + if (value != null) + { + dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Key, value)); + } + } + + return CreateInstance(dynamicPropertyWithValues); + } + + internal static IEnumerable ToDynamicJsonClassArray(this JArray? src, DynamicJsonClassOptions? options = null) + { + return src == null ? new object?[0] : ConvertJTokenArray(src, options); + } + + internal static object? ToDynamicClass(this JToken? src, DynamicJsonClassOptions? options = null) + { + return src == null ? null : GetResolverFor(src)(src, options); + } + + private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg is JObject asJObject) + { + return asJObject.ToDynamicClass(options); + } + + return GetResolverFor(arg)(arg, options); + } + + private static object PassThrough(JToken arg, DynamicJsonClassOptions? options) + { + return arg; + } + + private static Func GetResolverFor(JToken arg) + { + return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough; + } + + private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg.Type != JTokenType.Float) + { + throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float."); + } + + if (options?.FloatConvertBehavior == FloatBehavior.UseFloat) + { + try + { + return arg.Value(); + } + catch + { + return arg.Value(); + } + } + + if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal) + { + try + { + return arg.Value(); + } + catch + { + return arg.Value(); + } + } + + + return arg.Value(); + } + + private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg.Type != JTokenType.Integer) + { + throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int."); + } + + var longValue = arg.Value(); + + if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt) + { + if (longValue is >= int.MinValue and <= int.MaxValue) + { + return Convert.ToInt32(longValue); + } + } + + return longValue; + } + + private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null) + { + var resolver = GetResolverFor(arg); + if (resolver is null) + { + throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}."); + } + + return resolver(arg, options); + } + + private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null) + { + if (arg is not JArray array) + { + throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}."); + } + + var result = new List(); + foreach (var item in array) + { + result.Add(ConvertJObject(item)); + } + + var distinctType = FindSameTypeOf(result); + return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType); + } + + private static Type? FindSameTypeOf(IEnumerable src) + { + var types = src.Select(o => o?.GetType()).Distinct().OfType().ToArray(); + return types.Length == 1 ? types[0] : null; + } + + private static IEnumerable ConvertToTypedArray(IEnumerable src, Type newType) + { + var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType); + return (IEnumerable)method.Invoke(null, new object[] { src })!; + } + + private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!; + + private static T[] ConvertToTypedArrayGeneric(IEnumerable src) + { + return src.Cast().ToArray(); + } + + private static DynamicClass CreateInstance(IList dynamicPropertiesWithValue) + { + var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray()); + var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; + foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) + { + dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); + } + + return dynamicClass; + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs index 777261c8..8f6dfc57 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -5,7 +5,7 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; /// /// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonDocumentExtensions.cs /// -internal static class JsonDocumentExtensions +internal static class JsonDocumentExtensionsOld { public static T? ToObject(this JsonDocument document, JsonSerializerOptions options) { From 08558677c5c0262d6a9bc652dc0de4bdde338bb2 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Apr 2024 22:45:42 +0200 Subject: [PATCH 10/53] ???????????? --- .../ConsoleApp_net6.0.csproj | 1 + src-console/ConsoleApp_net6.0/Program.cs | 67 ++++-- .../Extensions/JObjectExtensions.cs | 14 +- .../Extensions/JObjectExtensions.cs | 208 ------------------ .../Extensions/JsonDocumentExtensions.cs | 206 ++++++++++++++++- .../Extensions/JsonDocumentExtensionsOld.cs | 15 ++ .../JsonExtensions.cs | 60 +++-- ...em.Linq.Dynamic.Core.SystemTextJson.csproj | 5 + 8 files changed, 323 insertions(+), 253 deletions(-) delete mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs diff --git a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj index 4718945a..86669ce8 100644 --- a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj +++ b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj @@ -9,6 +9,7 @@ + diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index 9a60a639..059d068e 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -4,6 +4,7 @@ using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core.NewtonsoftJson; using System.Linq.Expressions; +using System.Text.Json; using Newtonsoft.Json.Linq; namespace ConsoleApp_net6._0; @@ -22,6 +23,30 @@ public class Y class Program { static void Main(string[] args) + { + NewtonsoftJson(); + + return; + + Issue389DoesNotWork(); + return; + Issue389_Works(); + return; + + var q = new[] + { + new X { Key = "x" }, + new X { Key = "a" }, + new X { Key = "a", Contestants = new List { new Y() } } + }.AsQueryable(); + var groupByKey = q.GroupBy("Key"); + var selectQry = groupByKey.Select("new (Key, Sum(np(Contestants.Count, 0)) As TotalCount)").ToDynamicList(); + + Normal(); + Dynamic(); + } + + private static void NewtonsoftJson() { var array = JArray.Parse(@"[ { @@ -52,25 +77,39 @@ static void Main(string[] args) { Console.WriteLine(result); } + } - return; + private static void Json() + { + var doc = JsonDocument.Parse(@"[ + { + ""first"": 1, + ""City"": ""Paris"", + ""third"": ""test"" + }, + { + ""first"": 2, + ""City"": ""New York"", + ""third"": ""abc"" + }]"); - Issue389DoesNotWork(); - return; - Issue389_Works(); - return; + var where = doc.Where("City == @0", "Paris"); + foreach (var result in where) + { + Console.WriteLine(result["first"]); + } - var q = new[] + var select = doc.Select("City"); + foreach (var result in select) { - new X { Key = "x" }, - new X { Key = "a" }, - new X { Key = "a", Contestants = new List { new Y() } } - }.AsQueryable(); - var groupByKey = q.GroupBy("Key"); - var selectQry = groupByKey.Select("new (Key, Sum(np(Contestants.Count, 0)) As TotalCount)").ToDynamicList(); + Console.WriteLine(result); + } - Normal(); - Dynamic(); + var whereWithSelect = doc.Where("City == @0", "Paris").Select("first"); + foreach (var result in whereWithSelect) + { + Console.WriteLine(result); + } } private static void Issue389_Works() diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs index db90dcbf..0bc92fc9 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs @@ -35,10 +35,10 @@ private class JTokenResolvers : Dictionary o.Value() }, }; - internal static object? ToDynamicClass(this JValue src) - { - return src.Value; - } + //internal static object? ToDynamicClass(this JValue src) + //{ + // return src.Value; + //} internal static DynamicClass? ToDynamicClass(this JObject? src, DynamicJsonClassOptions? options = null) { @@ -47,18 +47,18 @@ private class JTokenResolvers : Dictionary(); + var dynamicPropertiesWithValue = new List(); foreach (var prop in src.Properties()) { var value = Resolvers[prop.Type](prop.Value, options); if (value != null) { - dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value)); + dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value)); } } - return CreateInstance(dynamicPropertyWithValues); + return CreateInstance(dynamicPropertiesWithValue); } internal static IEnumerable ToDynamicJsonClassArray(this JArray? src, DynamicJsonClassOptions? options = null) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs deleted file mode 100644 index 47ed5863..00000000 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JObjectExtensions.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq.Dynamic.Core.SystemTextJson.Models; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Nodes; -using JsonConverter.Abstractions.Models; - -namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; - -/// -/// Based on https://github.com/StefH/JsonConverter/blob/main/src/JsonConverter.Newtonsoft.Json/Extensions/JObjectExtensions.cs -/// -internal static class JsonDocumentExtensions -{ - private class JTokenResolvers : Dictionary> - { - } - - private static readonly JTokenResolvers Resolvers = new() - { - { JsonValueKind.Array, ConvertJTokenArray }, - { JsonValueKind.Boolean, (jToken, _) => jToken.Value() }, - { JsonValueKind.Bytes, (jToken, _) => jToken.Value() }, - { JsonValueKind.Date, (jToken, _) => jToken.Value() }, - { JsonValueKind.Float, ConvertJTokenFloat }, - { JsonValueKind.Guid, (jToken, _) => jToken.Value() }, - { JsonValueKind.Integer, ConvertJTokenInteger }, - { JsonValueKind.None, (_, _) => null }, - { JsonValueKind.Null, (_, _) => null }, - { JsonValueKind.Object, ConvertJObject }, - { JsonValueKind.Property, ConvertJTokenProperty }, - { JsonValueKind.String, (jToken, _) => jToken.Value() }, - { JsonValueKind.TimeSpan, (jToken, _) => jToken.Value() }, - { JsonValueKind.Undefined, (_, _) => null }, - { JsonValueKind.Uri, (o, _) => o.Value() }, - }; - - //internal static object? ToDynamicClass(this JValue src) - //{ - // return src.Value; - //} - - internal static DynamicClass? ToDynamicClass(this JsonDocument? src, DynamicJsonClassOptions? options = null) - { - if (src == null) - { - return null; - } - - var dynamicPropertyWithValues = new List(); - - foreach (var prop in src.RootElement.EnumerateObject()) - { - var value = Resolvers[prop.Value.ValueKind](prop.Value, options); - if (value != null) - { - dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Key, value)); - } - } - - return CreateInstance(dynamicPropertyWithValues); - } - - internal static IEnumerable ToDynamicJsonClassArray(this JArray? src, DynamicJsonClassOptions? options = null) - { - return src == null ? new object?[0] : ConvertJTokenArray(src, options); - } - - internal static object? ToDynamicClass(this JToken? src, DynamicJsonClassOptions? options = null) - { - return src == null ? null : GetResolverFor(src)(src, options); - } - - private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null) - { - if (arg is JObject asJObject) - { - return asJObject.ToDynamicClass(options); - } - - return GetResolverFor(arg)(arg, options); - } - - private static object PassThrough(JToken arg, DynamicJsonClassOptions? options) - { - return arg; - } - - private static Func GetResolverFor(JToken arg) - { - return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough; - } - - private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null) - { - if (arg.Type != JTokenType.Float) - { - throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float."); - } - - if (options?.FloatConvertBehavior == FloatBehavior.UseFloat) - { - try - { - return arg.Value(); - } - catch - { - return arg.Value(); - } - } - - if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal) - { - try - { - return arg.Value(); - } - catch - { - return arg.Value(); - } - } - - - return arg.Value(); - } - - private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null) - { - if (arg.Type != JTokenType.Integer) - { - throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int."); - } - - var longValue = arg.Value(); - - if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt) - { - if (longValue is >= int.MinValue and <= int.MaxValue) - { - return Convert.ToInt32(longValue); - } - } - - return longValue; - } - - private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null) - { - var resolver = GetResolverFor(arg); - if (resolver is null) - { - throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}."); - } - - return resolver(arg, options); - } - - private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null) - { - if (arg is not JArray array) - { - throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}."); - } - - var result = new List(); - foreach (var item in array) - { - result.Add(ConvertJObject(item)); - } - - var distinctType = FindSameTypeOf(result); - return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType); - } - - private static Type? FindSameTypeOf(IEnumerable src) - { - var types = src.Select(o => o?.GetType()).Distinct().OfType().ToArray(); - return types.Length == 1 ? types[0] : null; - } - - private static IEnumerable ConvertToTypedArray(IEnumerable src, Type newType) - { - var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType); - return (IEnumerable)method.Invoke(null, new object[] { src })!; - } - - private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!; - - private static T[] ConvertToTypedArrayGeneric(IEnumerable src) - { - return src.Cast().ToArray(); - } - - private static DynamicClass CreateInstance(IList dynamicPropertiesWithValue) - { - var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray()); - var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; - foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) - { - dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); - } - - return dynamicClass; - } -} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs index 8f6dfc57..44fa7df8 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -1,15 +1,205 @@ -using System.Text.Json; +using System.Collections; +using System.Collections.Generic; +using System.Linq.Dynamic.Core.SystemTextJson.Models; +using System.Reflection; +using System.Text.Json; +using JsonConverter.Abstractions.Models; namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; -/// -/// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonDocumentExtensions.cs -/// -internal static class JsonDocumentExtensionsOld +internal static class JsonDocumentExtensions { - public static T? ToObject(this JsonDocument document, JsonSerializerOptions options) + private class JTokenResolvers : Dictionary> { - var rawText = document.RootElement.GetRawText(); - return JsonSerializer.Deserialize(rawText, options); + } + + private static readonly JTokenResolvers Resolvers = new() + { + { JsonValueKind.Array, ConvertJTokenArray }, + { JsonValueKind.False, (_, _) => false }, + { JsonValueKind.True, (_, _) => true }, + // { JsonValueKind.Bytes, (jToken, _) => jToken.Value() }, + // { JsonValueKind.Date, (jToken, _) => jToken.Value() }, + // { JsonValueKind.Float, ConvertJTokenFloat }, + // { JsonValueKind.Guid, (jToken, _) => jToken.Value() }, + { JsonValueKind.Number, ConvertNumber }, + // { JsonValueKind.None, (_, _) => null }, + { JsonValueKind.Null, (_, _) => null }, + { JsonValueKind.Object, ConvertJObject }, + // { JsonValueKind.Property, ConvertJTokenProperty }, + { JsonValueKind.String, ConvertString }, + // { JsonValueKind.TimeSpan, (jToken, _) => jToken.Value() }, + { JsonValueKind.Undefined, (_, _) => null }, + // { JsonValueKind.Uri, (o, _) => o.Value() }, + }; + + //internal static object? ToDynamicClass(this JValue src) + //{ + // return src.Value; + //} + + internal static DynamicClass? ToDynamicClass(this JsonDocument? src, DynamicJsonClassOptions? options = null) + { + if (src == null) + { + return null; + } + + var dynamicPropertiesWithValue = new List(); + + foreach (var prop in src.RootElement.EnumerateObject()) + { + var value = Resolvers[prop.Value.ValueKind](prop.Value, options); + if (value != null) + { + dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value)); + } + } + + return CreateInstance(dynamicPropertiesWithValue); + } + + internal static IEnumerable ToDynamicJsonClassArray(this JsonElement? src, DynamicJsonClassOptions? options = null) + { + return src == null ? new object?[0] : ConvertJTokenArray(src.Value, options); + } + + internal static object? ToDynamicClass(this JsonElement? src, DynamicJsonClassOptions? options = null) + { + return src == null ? null : GetResolverFor(src.Value)(src.Value, options); + } + + private static object? ConvertJObject(JsonElement arg, DynamicJsonClassOptions? options = null) + { + //if (arg is JObject asJObject) + //{ + // return asJObject.ToDynamicClass(options); + //} + + return GetResolverFor(arg)(arg, options); + } + + private static object PassThrough(JsonElement arg, DynamicJsonClassOptions? options) + { + return arg; + } + + private static Func GetResolverFor(JsonElement arg) + { + return Resolvers.TryGetValue(arg.ValueKind, out var result) ? result : PassThrough; + } + + private static object? ConvertString(JsonElement arg, DynamicJsonClassOptions? options = null) + { + if (arg.TryGetGuid(out var guid)) + { + return guid; + } + + if (arg.TryGetDateTime(out var dt)) + { + return dt; + } + + if (arg.TryGetByte(out var @byte)) + { + return @byte; + } + + if (arg.TryGetBytesFromBase64(out var base64)) + { + return base64; + } + + return arg.GetString(); + } + + private static object ConvertNumber(JsonElement arg, DynamicJsonClassOptions? options = null) + { + //if (arg.ValueKind != JsonValueKind.Number) + //{ + // throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to Number."); + //} + + if (arg.TryGetInt32(out var int32)) + { + return int32; + } + + if (arg.TryGetInt64(out var int64)) + { + return int64; + } + + if (arg.TryGetDouble(out var @double)) + { + return @double; + } + + if (arg.TryGetDecimal(out var @decimal)) + { + return @decimal; + } + + throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to int, long, double or decimal."); + } + + //private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null) + //{ + // var resolver = GetResolverFor(arg); + // if (resolver is null) + // { + // throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}."); + // } + + // return resolver(arg, options); + //} + + private static IEnumerable ConvertJTokenArray(JsonElement arg, DynamicJsonClassOptions? options = null) + { + //if (arg is not JArray array) + //{ + // throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}."); + //} + + var result = new List(); + foreach (var item in arg.EnumerateArray()) + { + result.Add(ConvertJObject(item)); + } + + var distinctType = FindSameTypeOf(result); + return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType); + } + + private static Type? FindSameTypeOf(IEnumerable src) + { + var types = src.Select(o => o?.GetType()).Distinct().OfType().ToArray(); + return types.Length == 1 ? types[0] : null; + } + + private static IEnumerable ConvertToTypedArray(IEnumerable src, Type newType) + { + var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType); + return (IEnumerable)method.Invoke(null, new object[] { src })!; + } + + private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!; + + private static T[] ConvertToTypedArrayGeneric(IEnumerable src) + { + return src.Cast().ToArray(); + } + + private static DynamicClass CreateInstance(IList dynamicPropertiesWithValue) + { + var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray()); + var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; + foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) + { + dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); + } + + return dynamicClass; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs new file mode 100644 index 00000000..8f6dfc57 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; + +/// +/// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonDocumentExtensions.cs +/// +internal static class JsonDocumentExtensionsOld +{ + public static T? ToObject(this JsonDocument document, JsonSerializerOptions options) + { + var rawText = document.RootElement.GetRawText(); + return JsonSerializer.Deserialize(rawText, options); + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs index 2dc05e11..98e00afe 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs @@ -1,6 +1,8 @@ using System.Linq.Dynamic.Core.SystemTextJson.Config; +using System.Linq.Dynamic.Core.SystemTextJson.Extensions; using System.Linq.Dynamic.Core.Validation; using System.Text.Json; +using System.Text.Json.Nodes; namespace System.Linq.Dynamic.Core.SystemTextJson; @@ -63,12 +65,7 @@ public static JsonDocument Select(this JsonDocument source, JsonParsingConfig co Check.NotNull(source); Check.NotNull(config); Check.NotNullOrEmpty(selector); - - if (source.Count == 0) - { - return new JArray(); - } - + var queryable = ConvertToQueryable(source, config); return ToJArray(() => queryable.Select(config, selector, args)); } @@ -101,11 +98,6 @@ public static JsonDocument Where(this JsonDocument source, JsonParsingConfig con Check.NotNull(config); Check.NotNullOrEmpty(predicate); - if (source.Count == 0) - { - return new JArray(); - } - var queryable = ConvertToQueryable(source, config); return ToJArray(() => queryable.Where(config, predicate, args)); } @@ -116,19 +108,55 @@ public static JsonDocument Where(this JsonDocument source, JsonParsingConfig con /// /// The callback which returns a . /// - private static JsonDocument ToJArray(Func func) + private static JsonElement ToJArray(Func func) { - var array = new JsonDocument(); + var array = new JsonElement(); foreach (var dynamicElement in func()) { - var element = dynamicElement is DynamicClass dynamicClass ? JObject.FromObject(dynamicClass) : dynamicElement; - array.Add(element); + + var element = dynamicElement is DynamicClass dynamicClass ? JsonDocument..FromObject(dynamicClass) : dynamicElement; + //array.Add(element); + TryAddPropertyToArrayElements(array, "element", dynamicElement); } return array; } - private static IQueryable ConvertToQueryable(JsonDocument source, JsonParsingConfig config) + private static IQueryable ConvertToQueryable(JsonElement source, JsonParsingConfig config) { + if (source.ValueKind != JsonValueKind.Array) + { + throw new NotSupportedException("The source is not a JSON array."); + } + return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); } + + private static JsonNode TryAddPropertyToArrayElements(this JsonNode node, string name, TProperty value) + { + if (node is JsonArray array) + { + foreach (var obj in array.OfType()) + { + obj[name] = JsonSerializer.SerializeToNode(value); + } + } + + return node; + } + + private static JsonElement TryAddPropertyToArrayElements(this JsonElement element, string name, TProperty value) + { + return element.ValueKind == JsonValueKind.Array + ? JsonSerializer.SerializeToElement(element.Deserialize()!.TryAddPropertyToArrayElements(name, value)) + : element; + } + + private static object? TryAddPropertyToArrayElements(this object? obj, string name, TProperty value) => + obj switch + { + JsonElement e => e.TryAddPropertyToArrayElements(name, value), + JsonNode n => n.TryAddPropertyToArrayElements(name, value), + null => null, // JSON values that are null are deserialized to the c# null value, not some element or node of type null + _ => throw new ArgumentException("Unexpected type ${obj}"), + }; } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj index 80a9345e..f5c089ea 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -22,6 +22,11 @@ true + + + + + From e57d6ed6eb6db64604777c3ace0db2295d69be8d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 09:17:00 +0200 Subject: [PATCH 11/53] stj --- .../ConsoleApp_net6.0.csproj | 2 +- src-console/ConsoleApp_net6.0/Program.cs | 12 +-- .../JsonExtensions.cs | 14 ++- .../Extensions/JsonDocumentExtensions.cs | 81 ++++------------- .../Extensions/JsonDocumentExtensionsOld.cs | 15 ---- .../Extensions/JsonElementExtensions.cs | 15 ---- .../JsonExtensions.cs | 88 ++++++------------- .../Utils/JsonDocumentUtils.cs | 13 +++ .../Utils/JsonElementUtils.cs | 15 ++++ 9 files changed, 85 insertions(+), 170 deletions(-) delete mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs delete mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonDocumentUtils.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonElementUtils.cs diff --git a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj index 86669ce8..46108c0b 100644 --- a/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj +++ b/src-console/ConsoleApp_net6.0/ConsoleApp_net6.0.csproj @@ -9,7 +9,7 @@ - + diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index 059d068e..2831ac9f 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core.NewtonsoftJson; +using System.Linq.Dynamic.Core.SystemTextJson; using System.Linq.Expressions; using System.Text.Json; using Newtonsoft.Json.Linq; @@ -24,7 +25,8 @@ class Program { static void Main(string[] args) { - NewtonsoftJson(); + Json(); + // NewtonsoftJson(); return; @@ -94,19 +96,19 @@ private static void Json() }]"); var where = doc.Where("City == @0", "Paris"); - foreach (var result in where) + foreach (var result in where.RootElement.EnumerateArray()) { - Console.WriteLine(result["first"]); + Console.WriteLine(result.GetProperty("first")); } var select = doc.Select("City"); - foreach (var result in select) + foreach (var result in select.EnumerateArray()) { Console.WriteLine(result); } var whereWithSelect = doc.Where("City == @0", "Paris").Select("first"); - foreach (var result in whereWithSelect) + foreach (var result in whereWithSelect.EnumerateArray()) { Console.WriteLine(result); } diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs index f2bd7fda..f5ccdd52 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs @@ -30,7 +30,7 @@ public static bool All(this JArray source, JsonParsingConfig config, string pred Check.NotNull(config); Check.NotEmpty(predicate); - var queryable = ConvertToQueryable(source, config); + var queryable = ToQueryable(source, config); return queryable.All(config, predicate, args); } #endregion All @@ -67,7 +67,7 @@ public static JArray Select(this JArray source, JsonParsingConfig config, string return new JArray(); } - var queryable = ConvertToQueryable(source, config); + var queryable = ToQueryable(source, config); return ToJArray(() => queryable.Select(config, selector, args)); } #endregion Select @@ -104,16 +104,11 @@ public static JArray Where(this JArray source, JsonParsingConfig config, string return new JArray(); } - var queryable = ConvertToQueryable(source, config); + var queryable = ToQueryable(source, config); return ToJArray(() => queryable.Where(config, predicate, args)); } #endregion Where - /// - /// Convert the dynamic results to a JArray. - /// - /// The callback which returns a . - /// private static JArray ToJArray(Func func) { var array = new JArray(); @@ -122,10 +117,11 @@ private static JArray ToJArray(Func func) var element = dynamicElement is DynamicClass dynamicClass ? JObject.FromObject(dynamicClass) : dynamicElement; array.Add(element); } + return array; } - private static IQueryable ConvertToQueryable(JArray source, JsonParsingConfig config) + private static IQueryable ToQueryable(JArray source, JsonParsingConfig config) { return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); } diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs index 44fa7df8..d7b6d8a2 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -15,30 +15,17 @@ private class JTokenResolvers : Dictionary false }, { JsonValueKind.True, (_, _) => true }, - // { JsonValueKind.Bytes, (jToken, _) => jToken.Value() }, - // { JsonValueKind.Date, (jToken, _) => jToken.Value() }, - // { JsonValueKind.Float, ConvertJTokenFloat }, - // { JsonValueKind.Guid, (jToken, _) => jToken.Value() }, { JsonValueKind.Number, ConvertNumber }, - // { JsonValueKind.None, (_, _) => null }, { JsonValueKind.Null, (_, _) => null }, - { JsonValueKind.Object, ConvertJObject }, - // { JsonValueKind.Property, ConvertJTokenProperty }, + { JsonValueKind.Object, ConvertJsonElement }, { JsonValueKind.String, ConvertString }, - // { JsonValueKind.TimeSpan, (jToken, _) => jToken.Value() }, - { JsonValueKind.Undefined, (_, _) => null }, - // { JsonValueKind.Uri, (o, _) => o.Value() }, + { JsonValueKind.Undefined, (_, _) => null } }; - //internal static object? ToDynamicClass(this JValue src) - //{ - // return src.Value; - //} - - internal static DynamicClass? ToDynamicClass(this JsonDocument? src, DynamicJsonClassOptions? options = null) + internal static DynamicClass? ToDynamicClass(this JsonElement? src, DynamicJsonClassOptions? options = null) { if (src == null) { @@ -47,7 +34,7 @@ private class JTokenResolvers : Dictionary(); - foreach (var prop in src.RootElement.EnumerateObject()) + foreach (var prop in src.Value.EnumerateObject()) { var value = Resolvers[prop.Value.ValueKind](prop.Value, options); if (value != null) @@ -59,24 +46,14 @@ private class JTokenResolvers : Dictionary() : ConvertJsonElementToEnumerable(src.Value, options); } - private static object? ConvertJObject(JsonElement arg, DynamicJsonClassOptions? options = null) + private static object? ConvertJsonElement(JsonElement arg, DynamicJsonClassOptions? options = null) { - //if (arg is JObject asJObject) - //{ - // return asJObject.ToDynamicClass(options); - //} - - return GetResolverFor(arg)(arg, options); + return arg.ValueKind == JsonValueKind.Object ? ToDynamicClass(arg, options) : GetResolverFor(arg)(arg, options); } private static object PassThrough(JsonElement arg, DynamicJsonClassOptions? options) @@ -101,26 +78,11 @@ private static object PassThrough(JsonElement arg, DynamicJsonClassOptions? opti return dt; } - if (arg.TryGetByte(out var @byte)) - { - return @byte; - } - - if (arg.TryGetBytesFromBase64(out var base64)) - { - return base64; - } - return arg.GetString(); } private static object ConvertNumber(JsonElement arg, DynamicJsonClassOptions? options = null) { - //if (arg.ValueKind != JsonValueKind.Number) - //{ - // throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to Number."); - //} - if (arg.TryGetInt32(out var int32)) { return int32; @@ -141,31 +103,20 @@ private static object ConvertNumber(JsonElement arg, DynamicJsonClassOptions? op return @decimal; } + if (arg.TryGetByte(out var @byte)) + { + return @byte; + } + throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to int, long, double or decimal."); } - //private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null) - //{ - // var resolver = GetResolverFor(arg); - // if (resolver is null) - // { - // throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}."); - // } - - // return resolver(arg, options); - //} - - private static IEnumerable ConvertJTokenArray(JsonElement arg, DynamicJsonClassOptions? options = null) + private static IEnumerable ConvertJsonElementToEnumerable(JsonElement arg, DynamicJsonClassOptions? options = null) { - //if (arg is not JArray array) - //{ - // throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}."); - //} - var result = new List(); foreach (var item in arg.EnumerateArray()) { - result.Add(ConvertJObject(item)); + result.Add(ConvertJsonElement(item)); } var distinctType = FindSameTypeOf(result); diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs deleted file mode 100644 index 8f6dfc57..00000000 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensionsOld.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json; - -namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; - -/// -/// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonDocumentExtensions.cs -/// -internal static class JsonDocumentExtensionsOld -{ - public static T? ToObject(this JsonDocument document, JsonSerializerOptions options) - { - var rawText = document.RootElement.GetRawText(); - return JsonSerializer.Deserialize(rawText, options); - } -} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs deleted file mode 100644 index 9394d90b..00000000 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonElementExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json; - -namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; - -/// -/// Copied from https://github.com/StefH/JsonConverter/tree/main/src/JsonConverter.System.Text.Json/Extensions/JsonElementExtensions.cs -/// -internal static class JsonElementExtensions -{ - public static T? ToObject(this JsonElement element, JsonSerializerOptions options) - { - var rawText = element.GetRawText(); - return JsonSerializer.Deserialize(rawText, options); - } -} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs index 98e00afe..65e6f6fd 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs @@ -1,8 +1,9 @@ -using System.Linq.Dynamic.Core.SystemTextJson.Config; +using System.Collections.Generic; +using System.Linq.Dynamic.Core.SystemTextJson.Config; using System.Linq.Dynamic.Core.SystemTextJson.Extensions; +using System.Linq.Dynamic.Core.SystemTextJson.Utils; using System.Linq.Dynamic.Core.Validation; using System.Text.Json; -using System.Text.Json.Nodes; namespace System.Linq.Dynamic.Core.SystemTextJson; @@ -13,7 +14,7 @@ public static class JsonExtensions { #region All /// Determines whether all the elements of a sequence satisfy a condition. - /// A sequence whose elements to test for a condition. + /// The source /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. @@ -23,7 +24,7 @@ public static bool All(this JsonDocument source, string predicate, params object } /// Determines whether all the elements of a sequence satisfy a condition. - /// A sequence whose elements to test for a condition. + /// The source /// The . /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. @@ -34,7 +35,7 @@ public static bool All(this JsonDocument source, JsonParsingConfig config, strin Check.NotNull(config); Check.NotEmpty(predicate); - var queryable = ConvertToQueryable(source, config); + var queryable = ToQueryable(source.RootElement, config); return queryable.All(config, predicate, args); } #endregion All @@ -43,7 +44,7 @@ public static bool All(this JsonDocument source, JsonParsingConfig config, strin /// /// Projects each element of a sequence into a new form. /// - /// A sequence of values to project. + /// The source /// A projection string expression to apply to each element. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. @@ -55,19 +56,19 @@ public static JsonDocument Select(this JsonDocument source, string selector, par /// /// Projects each element of a sequence into a new form. /// - /// A sequence of values to project. + /// The source /// The . /// A projection string expression to apply to each element. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// An whose elements are the result of invoking a projection string on each element of source. + /// An whose elements are the result of invoking a projection string on each element of source. public static JsonDocument Select(this JsonDocument source, JsonParsingConfig config, string selector, params object?[] args) { Check.NotNull(source); Check.NotNull(config); Check.NotNullOrEmpty(selector); - - var queryable = ConvertToQueryable(source, config); - return ToJArray(() => queryable.Select(config, selector, args)); + + var queryable = ToQueryable(source.RootElement, config); + return ToJsonDocumentArray(() => queryable.Select(config, selector, args)); } #endregion Select @@ -75,10 +76,10 @@ public static JsonDocument Select(this JsonDocument source, JsonParsingConfig co /// /// Filters a sequence of values based on a predicate. /// - /// A to filter. + /// The source /// An expression string to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A that contains elements from the input sequence that satisfy the condition specified by predicate. + /// A that contains elements from the input sequence that satisfy the condition specified by predicate. public static JsonDocument Where(this JsonDocument source, string predicate, params object?[] args) { return Where(source, JsonParsingConfig.Default, predicate, args); @@ -87,76 +88,43 @@ public static JsonDocument Where(this JsonDocument source, string predicate, par /// /// Filters a sequence of values based on a predicate. /// - /// A to filter. + /// The source /// The . /// An expression string to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A that contains elements from the input sequence that satisfy the condition specified by predicate. + /// A that contains elements from the input sequence that satisfy the condition specified by predicate. public static JsonDocument Where(this JsonDocument source, JsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); Check.NotNullOrEmpty(predicate); - var queryable = ConvertToQueryable(source, config); - return ToJArray(() => queryable.Where(config, predicate, args)); + var queryable = ToQueryable(source.RootElement, config); + return ToJsonDocumentArray(() => queryable.Where(config, predicate, args)); } #endregion Where - /// - /// Convert the dynamic results to a JArray. - /// - /// The callback which returns a . - /// - private static JsonElement ToJArray(Func func) + #region Private Methods + private static JsonDocument ToJsonDocumentArray(Func func) { - var array = new JsonElement(); + var array = new List(); foreach (var dynamicElement in func()) { - - var element = dynamicElement is DynamicClass dynamicClass ? JsonDocument..FromObject(dynamicClass) : dynamicElement; - //array.Add(element); - TryAddPropertyToArrayElements(array, "element", dynamicElement); + var element = dynamicElement is DynamicClass dynamicClass ? JsonElementUtils.FromObject(dynamicClass) : dynamicElement; + array.Add(element); } - return array; + + return JsonDocumentUtils.FromObject(array); } - private static IQueryable ConvertToQueryable(JsonElement source, JsonParsingConfig config) + private static IQueryable ToQueryable(JsonElement source, JsonParsingConfig config) { if (source.ValueKind != JsonValueKind.Array) { throw new NotSupportedException("The source is not a JSON array."); } - return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); - } - - private static JsonNode TryAddPropertyToArrayElements(this JsonNode node, string name, TProperty value) - { - if (node is JsonArray array) - { - foreach (var obj in array.OfType()) - { - obj[name] = JsonSerializer.SerializeToNode(value); - } - } - - return node; - } - - private static JsonElement TryAddPropertyToArrayElements(this JsonElement element, string name, TProperty value) - { - return element.ValueKind == JsonValueKind.Array - ? JsonSerializer.SerializeToElement(element.Deserialize()!.TryAddPropertyToArrayElements(name, value)) - : element; + return JsonDocumentExtensions.ToDynamicJsonClassArray(source, config.DynamicJsonClassOptions).AsQueryable(); } - - private static object? TryAddPropertyToArrayElements(this object? obj, string name, TProperty value) => - obj switch - { - JsonElement e => e.TryAddPropertyToArrayElements(name, value), - JsonNode n => n.TryAddPropertyToArrayElements(name, value), - null => null, // JSON values that are null are deserialized to the c# null value, not some element or node of type null - _ => throw new ArgumentException("Unexpected type ${obj}"), - }; + #endregion } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonDocumentUtils.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonDocumentUtils.cs new file mode 100644 index 00000000..48206885 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonDocumentUtils.cs @@ -0,0 +1,13 @@ +using System.Text.Json; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Utils; + +internal static class JsonDocumentUtils +{ + internal static JsonDocument FromObject(object value) + { + var jsonString = JsonSerializer.Serialize(value); + + return JsonDocument.Parse(jsonString); + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonElementUtils.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonElementUtils.cs new file mode 100644 index 00000000..1d89aa7b --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/JsonElementUtils.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Utils; + +internal static class JsonElementUtils +{ + internal static JsonElement FromObject(object value) + { + var jsonString = JsonSerializer.Serialize(value); + + var doc = JsonDocument.Parse(jsonString); + + return doc.RootElement; + } +} \ No newline at end of file From 23faa1ef29b9de55ca0d0ded3b5e2b9183abae0c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 09:26:04 +0200 Subject: [PATCH 12/53] Run Json Tests .NET 8 --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4c26683..358bdc58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,11 @@ jobs: dotnet build ./test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj -c Release -p:buildType=azure-pipelines-ci dotnet test ./test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj -c Release -p:buildType=azure-pipelines-ci --no-build + - name: Run Json Tests .NET 8 + run: | + dotnet build ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci + dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci --no-build + - name: Run Tests net7.0 run: | dotnet build ./test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj -c Release -p:buildType=azure-pipelines-ci @@ -42,9 +47,4 @@ jobs: - name: Run Tests net452 run: | dotnet build ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj -c Release -f net452 -p:buildType=azure-pipelines-ci - dotnet test ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj -c Release -f net452 -p:buildType=azure-pipelines-ci --no-build - - - name: Run Json Tests .NET 8 - run: | - dotnet build ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci - dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci --no-build + dotnet test ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj -c Release -f net452 -p:buildType=azure-pipelines-ci --no-build \ No newline at end of file From f4447957e59a5d57e61868a3de604d10f56e982c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 11:03:30 +0200 Subject: [PATCH 13/53] any --- .github/workflows/ci.yml | 2 + System.Linq.Dynamic.Core.sln | 19 ++++ src-console/ConsoleApp_net6.0/Program.cs | 6 +- ...nfig.cs => NewtonsoftJsonParsingConfig.cs} | 4 +- ...ensions.cs => NewtonsoftJsonExtensions.cs} | 83 ++++++++++++++--- ...nfig.cs => SystemTextJsonParsingConfig.cs} | 4 +- ...ensions.cs => SystemTextJsonExtensions.cs} | 90 ++++++++++++++++--- .../NewtonsoftJsonTests.cs | 24 +++++ ...q.Dynamic.Core.NewtonsoftJson.Tests.csproj | 9 +- ...q.Dynamic.Core.SystemTextJson.Tests.csproj | 34 +++---- .../SystemTextJsonTests.cs | 34 +++++-- 11 files changed, 241 insertions(+), 68 deletions(-) rename src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/{JsonParsingConfig.cs => NewtonsoftJsonParsingConfig.cs} (58%) rename src/System.Linq.Dynamic.Core.NewtonsoftJson/{JsonExtensions.cs => NewtonsoftJsonExtensions.cs} (59%) rename src/System.Linq.Dynamic.Core.SystemTextJson/Config/{JsonParsingConfig.cs => SystemTextJsonParsingConfig.cs} (58%) rename src/System.Linq.Dynamic.Core.SystemTextJson/{JsonExtensions.cs => SystemTextJsonExtensions.cs} (60%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 358bdc58..63fd268d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,8 @@ jobs: run: | dotnet build ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci --no-build + dotnet build ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci + dotnet test ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj -c Release -f net8 -p:buildType=azure-pipelines-ci --no-build - name: Run Tests net7.0 run: | diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index e0f3907d..eb852c85 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -154,6 +154,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.Ne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.SystemTextJson", "src\System.Linq.Dynamic.Core.SystemTextJson\System.Linq.Dynamic.Core.SystemTextJson.csproj", "{FA01CE15-315A-499E-AFC2-955CA7EB45FF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.SystemTextJson.Tests", "test\System.Linq.Dynamic.Core.SystemTextJson.Tests\System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj", "{D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -984,6 +986,22 @@ Global {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x64.Build.0 = Release|Any CPU {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x86.ActiveCfg = Release|Any CPU {FA01CE15-315A-499E-AFC2-955CA7EB45FF}.Release|x86.Build.0 = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|ARM.Build.0 = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|x64.Build.0 = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Debug|x86.Build.0 = Debug|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|Any CPU.Build.0 = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|ARM.ActiveCfg = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|ARM.Build.0 = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x64.ActiveCfg = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x64.Build.0 = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x86.ActiveCfg = Release|Any CPU + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1040,6 +1058,7 @@ Global {8C5851B8-5C47-4229-AB55-D4252703598E} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} {FA01CE15-315A-499E-AFC2-955CA7EB45FF} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index 2831ac9f..8c5af2f6 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -26,7 +26,7 @@ class Program static void Main(string[] args) { Json(); - // NewtonsoftJson(); + NewtonsoftJson(); return; @@ -102,13 +102,13 @@ private static void Json() } var select = doc.Select("City"); - foreach (var result in select.EnumerateArray()) + foreach (var result in select.RootElement.EnumerateArray()) { Console.WriteLine(result); } var whereWithSelect = doc.Where("City == @0", "Paris").Select("first"); - foreach (var result in whereWithSelect.EnumerateArray()) + foreach (var result in whereWithSelect.RootElement.EnumerateArray()) { Console.WriteLine(result); } diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/JsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs similarity index 58% rename from src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/JsonParsingConfig.cs rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs index 3da81b59..4b0156fd 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/JsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs @@ -2,9 +2,9 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Config; -public class JsonParsingConfig : ParsingConfig +public class NewtonsoftJsonParsingConfig : ParsingConfig { - public static JsonParsingConfig Default { get; } = new(); + public static NewtonsoftJsonParsingConfig Default { get; } = new(); public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs similarity index 59% rename from src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs rename to src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index f5ccdd52..150af939 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -1,11 +1,12 @@ using System.Linq.Dynamic.Core.NewtonsoftJson.Config; using System.Linq.Dynamic.Core.NewtonsoftJson.Extensions; using System.Linq.Dynamic.Core.Validation; +using System.Linq.Expressions; using Newtonsoft.Json.Linq; namespace System.Linq.Dynamic.Core.NewtonsoftJson; -public static class JsonExtensions +public static class NewtonsoftJsonExtensions { #region All /// Determines whether all the elements of a sequence satisfy a condition. @@ -15,16 +16,16 @@ public static class JsonExtensions /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. public static bool All(this JArray source, string predicate, params object?[] args) { - return All(source, JsonParsingConfig.Default, predicate, args); + return All(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } /// Determines whether all the elements of a sequence satisfy a condition. /// A sequence whose elements to test for a condition. - /// The . + /// The . /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. - public static bool All(this JArray source, JsonParsingConfig config, string predicate, params object?[] args) + public static bool All(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -35,6 +36,64 @@ public static bool All(this JArray source, JsonParsingConfig config, string pred } #endregion All + #region Any + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JArray source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Any(); + } + + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return queryable.Any(config, predicate, args); + } + + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JArray source, string predicate, params object?[] args) + { + return Any(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// A Lambda Expression. + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Any(lambda); + } + #endregion Any + #region Select /// /// Projects each element of a sequence into a new form. @@ -45,18 +104,18 @@ public static bool All(this JArray source, JsonParsingConfig config, string pred /// An whose elements are the result of invoking a projection string on each element of source. public static JArray Select(this JArray source, string selector, params object?[] args) { - return Select(source, JsonParsingConfig.Default, selector, args); + return Select(source, NewtonsoftJsonParsingConfig.Default, selector, args); } /// /// Projects each element of a sequence into a new form. /// /// A sequence of values to project. - /// The . + /// The . /// A projection string expression to apply to each element. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. - public static JArray Select(this JArray source, JsonParsingConfig config, string selector, params object?[] args) + public static JArray Select(this JArray source, NewtonsoftJsonParsingConfig config, string selector, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -82,18 +141,18 @@ public static JArray Select(this JArray source, JsonParsingConfig config, string /// A that contains elements from the input sequence that satisfy the condition specified by predicate. public static JArray Where(this JArray source, string predicate, params object?[] args) { - return Where(source, JsonParsingConfig.Default, predicate, args); + return Where(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } /// /// Filters a sequence of values based on a predicate. /// /// A to filter. - /// The . + /// The . /// An expression string to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A that contains elements from the input sequence that satisfy the condition specified by predicate. - public static JArray Where(this JArray source, JsonParsingConfig config, string predicate, params object?[] args) + public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -121,8 +180,8 @@ private static JArray ToJArray(Func func) return array; } - private static IQueryable ToQueryable(JArray source, JsonParsingConfig config) + private static IQueryable ToQueryable(JArray source, NewtonsoftJsonParsingConfig? config = null) { - return source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable(); + return source.ToDynamicJsonClassArray(config?.DynamicJsonClassOptions).AsQueryable(); } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs similarity index 58% rename from src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs rename to src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs index 49e21f82..c52c5078 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/JsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs @@ -2,9 +2,9 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Config; -public class JsonParsingConfig : ParsingConfig +public class SystemTextJsonParsingConfig : ParsingConfig { - public static JsonParsingConfig Default { get; } = new(); + public static SystemTextJsonParsingConfig Default { get; } = new(); public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs similarity index 60% rename from src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs rename to src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 65e6f6fd..1f7a85bf 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/JsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -3,6 +3,7 @@ using System.Linq.Dynamic.Core.SystemTextJson.Extensions; using System.Linq.Dynamic.Core.SystemTextJson.Utils; using System.Linq.Dynamic.Core.Validation; +using System.Linq.Expressions; using System.Text.Json; namespace System.Linq.Dynamic.Core.SystemTextJson; @@ -10,36 +11,97 @@ namespace System.Linq.Dynamic.Core.SystemTextJson; /// /// /// -public static class JsonExtensions +public static class SystemTextJsonExtensions { #region All - /// Determines whether all the elements of a sequence satisfy a condition. + /// + /// Determines whether all the elements of a sequence satisfy a condition. + /// /// The source /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. public static bool All(this JsonDocument source, string predicate, params object?[] args) { - return All(source, JsonParsingConfig.Default, predicate, args); + return All(source, SystemTextJsonParsingConfig.Default, predicate, args); } - /// Determines whether all the elements of a sequence satisfy a condition. + /// + /// Determines whether all the elements of a sequence satisfy a condition. + /// /// The source - /// The . + /// The . /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. - public static bool All(this JsonDocument source, JsonParsingConfig config, string predicate, params object?[] args) + public static bool All(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); - Check.NotEmpty(predicate); var queryable = ToQueryable(source.RootElement, config); return queryable.All(config, predicate, args); } #endregion All + #region Any + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source.RootElement); + return queryable.Any(); + } + + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source.RootElement, config); + return queryable.Any(config, predicate, args); + } + + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JsonDocument source, string predicate, params object?[] args) + { + return Any(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Determines whether a sequence contains any elements. + /// + /// The source + /// A Lambda Expression. + /// true if the source sequence contains any elements; otherwise, false. + public static bool Any(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source.RootElement); + return queryable.Any(lambda); + } + #endregion Any + #region Select /// /// Projects each element of a sequence into a new form. @@ -50,18 +112,18 @@ public static bool All(this JsonDocument source, JsonParsingConfig config, strin /// An whose elements are the result of invoking a projection string on each element of source. public static JsonDocument Select(this JsonDocument source, string selector, params object?[] args) { - return Select(source, JsonParsingConfig.Default, selector, args); + return Select(source, SystemTextJsonParsingConfig.Default, selector, args); } /// /// Projects each element of a sequence into a new form. /// /// The source - /// The . + /// The . /// A projection string expression to apply to each element. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. - public static JsonDocument Select(this JsonDocument source, JsonParsingConfig config, string selector, params object?[] args) + public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -82,7 +144,7 @@ public static JsonDocument Select(this JsonDocument source, JsonParsingConfig co /// A that contains elements from the input sequence that satisfy the condition specified by predicate. public static JsonDocument Where(this JsonDocument source, string predicate, params object?[] args) { - return Where(source, JsonParsingConfig.Default, predicate, args); + return Where(source, SystemTextJsonParsingConfig.Default, predicate, args); } /// @@ -93,7 +155,7 @@ public static JsonDocument Where(this JsonDocument source, string predicate, par /// An expression string to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A that contains elements from the input sequence that satisfy the condition specified by predicate. - public static JsonDocument Where(this JsonDocument source, JsonParsingConfig config, string predicate, params object?[] args) + public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -117,14 +179,14 @@ private static JsonDocument ToJsonDocumentArray(Func func) return JsonDocumentUtils.FromObject(array); } - private static IQueryable ToQueryable(JsonElement source, JsonParsingConfig config) + private static IQueryable ToQueryable(JsonElement source, SystemTextJsonParsingConfig? config = null) { if (source.ValueKind != JsonValueKind.Array) { throw new NotSupportedException("The source is not a JSON array."); } - return JsonDocumentExtensions.ToDynamicJsonClassArray(source, config.DynamicJsonClassOptions).AsQueryable(); + return JsonDocumentExtensions.ToDynamicJsonClassArray(source, config?.DynamicJsonClassOptions).AsQueryable(); } #endregion } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index d1340822..0bed66af 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -29,4 +29,28 @@ public void All() // Assert result.Should().BeTrue(); } + + [Fact] + public void Any() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var jArray = JArray.Parse(json); + + // Act + var result = jArray.Any("Age > 20"); + + // Assert + result.Should().BeTrue(); + } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj index 07df4d23..fe7bd9b5 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj @@ -2,7 +2,6 @@ Stef Heyenrath net452;netcoreapp3.1;net8.0 - full True latest @@ -22,11 +21,10 @@ runtime; build; native; contentfiles; analyzers - + - all runtime; build; native; contentfiles; analyzers @@ -35,11 +33,6 @@ all runtime; build; native; contentfiles; analyzers - - - - - \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj index 07df4d23..0dcf715d 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj @@ -1,45 +1,37 @@  Stef Heyenrath - net452;netcoreapp3.1;net8.0 - + net6.0;net8.0 full True latest enable ../../src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk - {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E} - - - - - - + all runtime; build; native; contentfiles; analyzers - - + + - - - + + all runtime; build; native; contentfiles; analyzers - + all runtime; build; native; contentfiles; analyzers - - - - - - + + + + + \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 17e9347f..65daa2b7 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -1,10 +1,8 @@ -using System.Linq; -using System.Linq.Dynamic.Core.NewtonsoftJson; +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json.Linq; using Xunit; -namespace System.Linq.Dynamic.Core.Tests.Json; +namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; public class NewtonsoftJsonTests { @@ -23,10 +21,34 @@ public void All() } ]"; - var jArray = JArray.Parse(json); + var doc = JsonDocument.Parse(json); // Act - var result = jArray.All("Age > 20"); + var result = doc.All("Age > 20"); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public void Any() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var doc = JsonDocument.Parse(json); + + // Act + var result = doc.Any("Age > 20"); // Assert result.Should().BeTrue(); From 08b5274d8fe48ceeb6ec85ec8ac835687dae967a Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 11:11:13 +0200 Subject: [PATCH 14/53] Aggregate --- .../NewtonsoftJsonExtensions.cs | 27 ++++++++++++- .../SystemTextJsonExtensions.cs | 40 ++++++++++++++----- .../NewtonsoftJsonTests.cs | 24 +++++++++++ .../SystemTextJsonTests.cs | 25 ++++++++++++ 4 files changed, 104 insertions(+), 12 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 150af939..086b67b8 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -8,8 +8,29 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson; public static class NewtonsoftJsonExtensions { + #region Aggregate + /// + /// Dynamically runs an aggregate function on the >. + /// + /// The > data source. + /// The name of the function to run. Can be Sum, Average, Min or Max. + /// The name of the property to aggregate over. + /// The value of the aggregate function run over the specified property. + public static object Aggregate(this JArray source, string function, string member) + { + Check.NotNull(source); + Check.NotEmpty(function); + Check.NotEmpty(member); + + var queryable = ToQueryable(source); + return queryable.Aggregate(function, member); + } + #endregion Aggregate + #region All - /// Determines whether all the elements of a sequence satisfy a condition. + /// + /// Determines whether all the elements of a sequence satisfy a condition. + /// /// A sequence whose elements to test for a condition. /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. @@ -19,7 +40,9 @@ public static bool All(this JArray source, string predicate, params object?[] ar return All(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - /// Determines whether all the elements of a sequence satisfy a condition. + /// + /// Determines whether all the elements of a sequence satisfy a condition. + /// /// A sequence whose elements to test for a condition. /// The . /// A function to test each element for a condition. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 1f7a85bf..7ff11279 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -13,6 +13,25 @@ namespace System.Linq.Dynamic.Core.SystemTextJson; /// public static class SystemTextJsonExtensions { + #region Aggregate + /// + /// Dynamically runs an aggregate function on the >. + /// + /// The > data source. + /// The name of the function to run. Can be Sum, Average, Min or Max. + /// The name of the property to aggregate over. + /// The value of the aggregate function run over the specified property. + public static object Aggregate(this JsonDocument source, string function, string member) + { + Check.NotNull(source); + Check.NotEmpty(function); + Check.NotEmpty(member); + + var queryable = ToQueryable(source); + return queryable.Aggregate(function, member); + } + #endregion Aggregate + #region All /// /// Determines whether all the elements of a sequence satisfy a condition. @@ -39,7 +58,7 @@ public static bool All(this JsonDocument source, SystemTextJsonParsingConfig con Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source.RootElement, config); + var queryable = ToQueryable(source, config); return queryable.All(config, predicate, args); } #endregion All @@ -54,7 +73,7 @@ public static bool Any(this JsonDocument source) { Check.NotNull(source); - var queryable = ToQueryable(source.RootElement); + var queryable = ToQueryable(source); return queryable.Any(); } @@ -71,7 +90,7 @@ public static bool Any(this JsonDocument source, SystemTextJsonParsingConfig con Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source.RootElement, config); + var queryable = ToQueryable(source, config); return queryable.Any(config, predicate, args); } @@ -97,7 +116,7 @@ public static bool Any(this JsonDocument source, LambdaExpression lambda) { Check.NotNull(source); - var queryable = ToQueryable(source.RootElement); + var queryable = ToQueryable(source); return queryable.Any(lambda); } #endregion Any @@ -129,7 +148,7 @@ public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsin Check.NotNull(config); Check.NotNullOrEmpty(selector); - var queryable = ToQueryable(source.RootElement, config); + var queryable = ToQueryable(source, config); return ToJsonDocumentArray(() => queryable.Select(config, selector, args)); } #endregion Select @@ -151,7 +170,7 @@ public static JsonDocument Where(this JsonDocument source, string predicate, par /// Filters a sequence of values based on a predicate. /// /// The source - /// The . + /// The . /// An expression string to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A that contains elements from the input sequence that satisfy the condition specified by predicate. @@ -161,7 +180,7 @@ public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsing Check.NotNull(config); Check.NotNullOrEmpty(predicate); - var queryable = ToQueryable(source.RootElement, config); + var queryable = ToQueryable(source, config); return ToJsonDocumentArray(() => queryable.Where(config, predicate, args)); } #endregion Where @@ -179,14 +198,15 @@ private static JsonDocument ToJsonDocumentArray(Func func) return JsonDocumentUtils.FromObject(array); } - private static IQueryable ToQueryable(JsonElement source, SystemTextJsonParsingConfig? config = null) + private static IQueryable ToQueryable(JsonDocument source, SystemTextJsonParsingConfig? config = null) { - if (source.ValueKind != JsonValueKind.Array) + var array = source.RootElement; + if (array.ValueKind != JsonValueKind.Array) { throw new NotSupportedException("The source is not a JSON array."); } - return JsonDocumentExtensions.ToDynamicJsonClassArray(source, config?.DynamicJsonClassOptions).AsQueryable(); + return JsonDocumentExtensions.ToDynamicJsonClassArray(array, config?.DynamicJsonClassOptions).AsQueryable(); } #endregion } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 0bed66af..aa5158c5 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -6,6 +6,30 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests; public class NewtonsoftJsonTests { + [Fact] + public void Aggregate() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var jArray = JArray.Parse(json); + + // Act + var result = jArray.Aggregate("Sum", "Age"); + + // Assert + result.Should().Be(55); + } + [Fact] public void All() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 65daa2b7..5a5d80ea 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -1,11 +1,36 @@ using System.Text.Json; using FluentAssertions; +using Newtonsoft.Json.Linq; using Xunit; namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; public class NewtonsoftJsonTests { + [Fact] + public void Aggregate() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var doc = JsonDocument.Parse(json); + + // Act + var result = doc.Aggregate("Sum", "Age"); + + // Assert + result.Should().Be(55); + } + [Fact] public void All() { From b3c597a70d61b1a207305e9724f9f0d25e99ae25 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 11:23:37 +0200 Subject: [PATCH 15/53] where-select tests --- .../SystemTextJsonExtensions.cs | 14 ++++- .../NewtonsoftJsonTests.cs | 51 ++++++++++++++++++ .../SystemTextJsonTests.cs | 52 ++++++++++++++++++- 3 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 7ff11279..fdd769e3 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -140,7 +140,7 @@ public static JsonDocument Select(this JsonDocument source, string selector, par /// The source /// The . /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args) { @@ -191,7 +191,17 @@ private static JsonDocument ToJsonDocumentArray(Func func) var array = new List(); foreach (var dynamicElement in func()) { - var element = dynamicElement is DynamicClass dynamicClass ? JsonElementUtils.FromObject(dynamicClass) : dynamicElement; + object element; + if (dynamicElement is DynamicClass dynamicClass) + { + element = JsonElementUtils.FromObject(dynamicClass); + } + else + { + element = dynamicElement; + } + + //var element = dynamicElement is DynamicClass dynamicClass ? JsonElementUtils.FromObject(dynamicClass) : dynamicElement; array.Add(element); } diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index aa5158c5..978f165c 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -77,4 +77,55 @@ public void Any() // Assert result.Should().BeTrue(); } + + [Fact] + public void Select() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var doc = JArray.Parse(json); + + // Act + var result = doc.Select("Name"); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().BeEquivalentTo("John", "Doe"); + } + + [Fact] + public void Where_Select() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var doc = JArray.Parse(json); + + // Act + var result = doc.Where("Age > 25").Select("Name"); + + // Assert + result.Should().HaveCount(1); + var first = result.First(); + first.Value().Should().Be("John"); + } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 5a5d80ea..7a7534d0 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -1,6 +1,5 @@ using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json.Linq; using Xunit; namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; @@ -78,4 +77,55 @@ public void Any() // Assert result.Should().BeTrue(); } + + [Fact] + public void Select() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var doc = JsonDocument.Parse(json); + + // Act + var result = doc.Select("Name"); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetString()); + array.Should().BeEquivalentTo("John", "Doe"); + } + + [Fact] + public void Where_Select() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 25 + } + ]"; + + var doc = JsonDocument.Parse(json); + + // Act + var result = doc.Where("Age > 25").Select("Name"); + + // Assert + var array = result.RootElement.EnumerateArray(); + array.Should().HaveCount(1); + array.First().GetString().Should().Be("John"); + } } \ No newline at end of file From ac117018a262206f90059b19227439f98e11337d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 11:31:35 +0200 Subject: [PATCH 16/53] Average --- .../NewtonsoftJsonExtensions.cs | 60 +++++++++++++++ .../SystemTextJsonExtensions.cs | 76 +++++++++++++++++-- .../NewtonsoftJsonTests.cs | 24 ++++++ .../SystemTextJsonTests.cs | 24 ++++++ 4 files changed, 176 insertions(+), 8 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 086b67b8..2590de63 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -117,6 +117,66 @@ public static bool Any(this JArray source, LambdaExpression lambda) } #endregion Any + #region Average + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// The average of the values in the sequence. + public static double Average(this JArray source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Average(); + } + + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// The average of the values in the sequence. + public static double Average(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotEmpty(predicate); + + var queryable = ToQueryable(source); + return queryable.Average(config, predicate, args); + } + + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// The average of the values in the sequence. + public static double Average(this JArray source, string predicate, params object?[] args) + { + return Average(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// A Lambda Expression. + /// The average of the values in the sequence. + public static double Average(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + Check.NotNull(lambda); + + var queryable = ToQueryable(source); + return queryable.Average(lambda); + } + #endregion Average + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index fdd769e3..bd79e841 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -38,7 +38,7 @@ public static object Aggregate(this JsonDocument source, string function, string /// /// The source /// A function to test each element for a condition. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. public static bool All(this JsonDocument source, string predicate, params object?[] args) { @@ -51,7 +51,7 @@ public static bool All(this JsonDocument source, string predicate, params object /// The source /// The . /// A function to test each element for a condition. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false. public static bool All(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { @@ -83,7 +83,7 @@ public static bool Any(this JsonDocument source) /// The source /// The . /// A function to test each element for a condition. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// true if the source sequence contains any elements; otherwise, false. public static bool Any(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { @@ -99,7 +99,7 @@ public static bool Any(this JsonDocument source, SystemTextJsonParsingConfig con /// /// The source /// A function to test each element for a condition. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// true if the source sequence contains any elements; otherwise, false. public static bool Any(this JsonDocument source, string predicate, params object?[] args) { @@ -121,13 +121,73 @@ public static bool Any(this JsonDocument source, LambdaExpression lambda) } #endregion Any + #region Average + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// The average of the values in the sequence. + public static double Average(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Average(); + } + + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// The average of the values in the sequence. + public static double Average(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotEmpty(predicate); + + var queryable = ToQueryable(source); + return queryable.Average(config, predicate, args); + } + + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// The average of the values in the sequence. + public static double Average(this JsonDocument source, string predicate, params object?[] args) + { + return Average(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the average of a sequence of numeric values. + /// + /// The source + /// A Lambda Expression. + /// The average of the values in the sequence. + public static double Average(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + Check.NotNull(lambda); + + var queryable = ToQueryable(source); + return queryable.Average(lambda); + } + #endregion Average + #region Select /// /// Projects each element of a sequence into a new form. /// /// The source /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// An whose elements are the result of invoking a projection string on each element of source. public static JsonDocument Select(this JsonDocument source, string selector, params object?[] args) { @@ -140,7 +200,7 @@ public static JsonDocument Select(this JsonDocument source, string selector, par /// The source /// The . /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// An whose elements are the result of invoking a projection string on each element of source. public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args) { @@ -159,7 +219,7 @@ public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsin /// /// The source /// An expression string to test each element for a condition. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// A that contains elements from the input sequence that satisfy the condition specified by predicate. public static JsonDocument Where(this JsonDocument source, string predicate, params object?[] args) { @@ -172,7 +232,7 @@ public static JsonDocument Where(this JsonDocument source, string predicate, par /// The source /// The . /// An expression string to test each element for a condition. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. /// A that contains elements from the input sequence that satisfy the condition specified by predicate. public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 978f165c..3b75a63e 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -78,6 +78,30 @@ public void Any() result.Should().BeTrue(); } + [Fact] + public void Average() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 40 + } + ]"; + + var jArray = JArray.Parse(json); + + // Act + var result = jArray.Average("Age"); + + // Assert + result.Should().BeApproximately(35, 0.00001); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 7a7534d0..734dae37 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -78,6 +78,30 @@ public void Any() result.Should().BeTrue(); } + [Fact] + public void Average() + { + // Arrange + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 40 + } + ]"; + + var doc = JsonDocument.Parse(json); + + // Act + var result = doc.Average("Age"); + + // Assert + result.Should().BeApproximately(35, 0.00001); + } + [Fact] public void Select() { From c03037b0bf419c62ecbf4aa2e1c28cc261520ac4 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 12:24:49 +0200 Subject: [PATCH 17/53] Cast --- .../NewtonsoftJsonExtensions.cs | 46 +++++++ .../SystemTextJsonExtensions.cs | 45 ++++++- .../NewtonsoftJsonTests.cs | 115 +++++------------ .../SystemTextJsonTests.cs | 117 +++++------------- 4 files changed, 153 insertions(+), 170 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 2590de63..c0b419a2 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -6,6 +6,9 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson; +/// +/// Extension methods for . +/// public static class NewtonsoftJsonExtensions { #region Aggregate @@ -177,6 +180,49 @@ public static double Average(this JArray source, LambdaExpression lambda) } #endregion Average + #region Cast + /// + /// Converts the elements of an to the specified type. + /// + /// The that contains the elements to be converted. + /// The type to convert the elements of source to. + /// An that contains each element of the source sequence converted to the specified type. + public static IQueryable Cast(this JArray source, Type type) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Cast(type); + } + + /// + /// Converts the elements of an to the specified type. + /// + /// The that contains the elements to be converted. + /// The . + /// The type to convert the elements of source to. + /// An that contains each element of the source sequence converted to the specified type. + public static IQueryable Cast(this JArray source, NewtonsoftJsonParsingConfig config, string typeName) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return queryable.Cast(typeName); + } + + /// + /// Converts the elements of an to the specified type. + /// + /// The that contains the elements to be converted. + /// The type to convert the elements of source to. + /// An that contains each element of the source sequence converted to the specified type. + public static IQueryable Cast(this JArray source, string typeName) + { + return Cast(source, NewtonsoftJsonParsingConfig.Default, typeName); + } + #endregion Cast + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index bd79e841..6c060594 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -9,7 +9,7 @@ namespace System.Linq.Dynamic.Core.SystemTextJson; /// -/// +/// Extension methods for . /// public static class SystemTextJsonExtensions { @@ -181,6 +181,49 @@ public static double Average(this JsonDocument source, LambdaExpression lambda) } #endregion Average + #region Cast + /// + /// Converts the elements of an to the specified type. + /// + /// The that contains the elements to be converted. + /// The type to convert the elements of source to. + /// An that contains each element of the source sequence converted to the specified type. + public static IQueryable Cast(this JsonDocument source, Type type) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Cast(type); + } + + /// + /// Converts the elements of an to the specified type. + /// + /// The that contains the elements to be converted. + /// The . + /// The type to convert the elements of source to. + /// An that contains each element of the source sequence converted to the specified type. + public static IQueryable Cast(this JsonDocument source, SystemTextJsonParsingConfig config, string typeName) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return queryable.Cast(typeName); + } + + /// + /// Converts the elements of an to the specified type. + /// + /// The that contains the elements to be converted. + /// The type to convert the elements of source to. + /// An that contains each element of the source sequence converted to the specified type. + public static IQueryable Cast(this JsonDocument source, string typeName) + { + return Cast(source, SystemTextJsonParsingConfig.Default, typeName); + } + #endregion Cast + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 3b75a63e..35e42911 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -6,49 +6,33 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests; public class NewtonsoftJsonTests { - [Fact] - public void Aggregate() - { - // Arrange - var json = @"[ + private const string ExampleJson = @"[ { ""Name"": ""John"", ""Age"": 30 }, { ""Name"": ""Doe"", - ""Age"": 25 + ""Age"": 40 } ]"; + private readonly JArray _source = JArray.Parse(ExampleJson); - var jArray = JArray.Parse(json); - + [Fact] + public void Aggregate() + { // Act - var result = jArray.Aggregate("Sum", "Age"); + var result = _source.Aggregate("Sum", "Age"); // Assert - result.Should().Be(55); + result.Should().Be(70); } [Fact] public void All() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var jArray = JArray.Parse(json); - // Act - var result = jArray.All("Age > 20"); + var result = _source.All("Age > 20"); // Assert result.Should().BeTrue(); @@ -57,22 +41,8 @@ public void All() [Fact] public void Any() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var jArray = JArray.Parse(json); - // Act - var result = jArray.Any("Age > 20"); + var result = _source.Any("Age > 20"); // Assert result.Should().BeTrue(); @@ -81,46 +51,37 @@ public void Any() [Fact] public void Average() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - } - ]"; - - var jArray = JArray.Parse(json); - // Act - var result = jArray.Average("Age"); + var result = _source.Average("Age"); // Assert result.Should().BeApproximately(35, 0.00001); } [Fact] - public void Select() + public void Cast() { // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; + var expected = new[] { "John", "Doe" }; + + // Act 1 + var resultType = _source.Select("Name").Cast(typeof(string)).ToDynamicArray(); - var doc = JArray.Parse(json); + // Assert 1 + resultType.Should().Contain(expected); + // Act 2 + var resultTypeName = _source.Select("Name").Cast("string").ToDynamicArray(); + + // Assert 2 + resultTypeName.Should().Contain(expected); + } + + [Fact] + public void Select() + { // Act - var result = doc.Select("Name"); + var result = _source.Select("Name"); // Assert var array = result.Select(x => x.Value()); @@ -130,26 +91,12 @@ public void Select() [Fact] public void Where_Select() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var doc = JArray.Parse(json); - // Act - var result = doc.Where("Age > 25").Select("Name"); + var result = _source.Where("Age > 30").Select("Name"); // Assert result.Should().HaveCount(1); var first = result.First(); - first.Value().Should().Be("John"); + first.Value().Should().Be("Doe"); } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 734dae37..5104df53 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -4,51 +4,35 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; -public class NewtonsoftJsonTests +public class SystemTextJsonTests { - [Fact] - public void Aggregate() - { - // Arrange - var json = @"[ + private const string ExampleJson = @"[ { ""Name"": ""John"", ""Age"": 30 }, { ""Name"": ""Doe"", - ""Age"": 25 + ""Age"": 40 } ]"; + private readonly JsonDocument _source = JsonDocument.Parse(ExampleJson); - var doc = JsonDocument.Parse(json); - + [Fact] + public void Aggregate() + { // Act - var result = doc.Aggregate("Sum", "Age"); + var result = _source.Aggregate("Sum", "Age"); // Assert - result.Should().Be(55); + result.Should().Be(70); } [Fact] public void All() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var doc = JsonDocument.Parse(json); - // Act - var result = doc.All("Age > 20"); + var result = _source.All("Age > 20"); // Assert result.Should().BeTrue(); @@ -57,22 +41,8 @@ public void All() [Fact] public void Any() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var doc = JsonDocument.Parse(json); - // Act - var result = doc.Any("Age > 20"); + var result = _source.Any("Age > 20"); // Assert result.Should().BeTrue(); @@ -81,46 +51,37 @@ public void Any() [Fact] public void Average() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - } - ]"; - - var doc = JsonDocument.Parse(json); - // Act - var result = doc.Average("Age"); + var result = _source.Average("Age"); // Assert result.Should().BeApproximately(35, 0.00001); } [Fact] - public void Select() + public void Cast() { // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; + var expected = new[] { "John", "Doe" }; + + // Act 1 + var resultType = _source.Select("Name").Cast(typeof(string)).ToDynamicArray(); - var doc = JsonDocument.Parse(json); + // Assert 1 + resultType.Should().Contain(expected); + // Act 2 + var resultTypeName = _source.Select("Name").Cast("string").ToDynamicArray(); + + // Assert 2 + resultTypeName.Should().Contain(expected); + } + + [Fact] + public void Select() + { // Act - var result = doc.Select("Name"); + var result = _source.Select("Name"); // Assert var array = result.RootElement.EnumerateArray().Select(x => x.GetString()); @@ -130,26 +91,12 @@ public void Select() [Fact] public void Where_Select() { - // Arrange - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 25 - } - ]"; - - var doc = JsonDocument.Parse(json); - // Act - var result = doc.Where("Age > 25").Select("Name"); + var result = _source.Where("Age > 30").Select("Name"); // Assert var array = result.RootElement.EnumerateArray(); array.Should().HaveCount(1); - array.First().GetString().Should().Be("John"); + array.First().GetString().Should().Be("Doe"); } } \ No newline at end of file From 53b7ab3ec75b278fa5dba01ebf14c84ec093a0ac Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 14:18:47 +0200 Subject: [PATCH 18/53] Count --- .../NewtonsoftJsonExtensions.cs | 58 +++++++++++++++++++ .../SystemTextJsonExtensions.cs | 58 +++++++++++++++++++ .../NewtonsoftJsonTests.cs | 16 +++++ .../SystemTextJsonTests.cs | 16 +++++ 4 files changed, 148 insertions(+) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index c0b419a2..59ded60f 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -223,6 +223,64 @@ public static IQueryable Cast(this JArray source, string typeName) } #endregion Cast + #region Count + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// The number of elements in the input sequence. + public static int Count(this JArray source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Count(); + } + + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The number of elements in the specified sequence that satisfies a condition. + public static int Count(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return queryable.Count(config, predicate, args); + } + + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The number of elements in the specified sequence that satisfies a condition. + public static int Count(this JArray source, string predicate, params object?[] args) + { + return Count(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// A cached Lambda Expression. + /// The number of elements in the specified sequence that satisfies a condition. + public static int Count(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Count(lambda); + } + #endregion Count + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 6c060594..5c166616 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -224,6 +224,64 @@ public static IQueryable Cast(this JsonDocument source, string typeName) } #endregion Cast + #region Count + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// The number of elements in the input sequence. + public static int Count(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Count(); + } + + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The number of elements in the specified sequence that satisfies a condition. + public static int Count(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return queryable.Count(config, predicate, args); + } + + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The number of elements in the specified sequence that satisfies a condition. + public static int Count(this JsonDocument source, string predicate, params object?[] args) + { + return Count(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the number of elements in a sequence. + /// + /// The that contains the elements to be counted. + /// A cached Lambda Expression. + /// The number of elements in the specified sequence that satisfies a condition. + public static int Count(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Count(lambda); + } + #endregion Count + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 35e42911..9a8d0b37 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -77,6 +77,22 @@ public void Cast() resultTypeName.Should().Contain(expected); } + [Fact] + public void Count() + { + // Act 1 + var result1 = _source.Count(); + + // Assert 1 + result1.Should().Be(2); + + // Act 2 + var result2 = _source.Count("Age > 30"); + + // Assert 2 + result2.Should().Be(1); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 5104df53..587e946c 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -77,6 +77,22 @@ public void Cast() resultTypeName.Should().Contain(expected); } + [Fact] + public void Count() + { + // Act 1 + var result1 = _source.Count(); + + // Assert 1 + result1.Should().Be(2); + + // Act 2 + var result2 = _source.Count("Age > 30"); + + // Assert 2 + result2.Should().Be(1); + } + [Fact] public void Select() { From 9080f0c99048c6aec4e7a4cd17bcf2ef829caa65 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 14:28:23 +0200 Subject: [PATCH 19/53] Distinct --- .../NewtonsoftJsonExtensions.cs | 44 +++++++++++++++ .../SystemTextJsonExtensions.cs | 56 +++++++++++++++---- .../NewtonsoftJsonTests.cs | 23 ++++++++ .../SystemTextJsonTests.cs | 23 ++++++++ 4 files changed, 135 insertions(+), 11 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 59ded60f..0266f2f8 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -281,6 +281,50 @@ public static int Count(this JArray source, LambdaExpression lambda) } #endregion Count + #region DefaultIfEmpty + /// + /// Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. + /// + /// The to return a default value for if empty. + /// An that contains default if source is empty; otherwise, source. + public static JArray DefaultIfEmpty(this JArray source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJArray(queryable.DefaultIfEmpty); + } + + /// + /// Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. + /// + /// The to return a default value for if empty. + /// The value to return if the sequence is empty. + /// An that contains defaultValue if source is empty; otherwise, source. + public static JArray DefaultIfEmpty(this JArray source, object? defaultValue) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJArray(() => queryable.DefaultIfEmpty(defaultValue)); + } + #endregion + + #region Distinct + /// + /// Returns distinct elements from a sequence by using the default equality comparer to compare values. + /// + /// The sequence to remove duplicate elements from. + /// An that contains distinct elements from the source sequence. + public static JArray Distinct(this JArray source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJArray(queryable.Distinct); + } + #endregion Distinct + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 5c166616..a97ca545 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -282,6 +282,50 @@ public static int Count(this JsonDocument source, LambdaExpression lambda) } #endregion Count + #region DefaultIfEmpty + /// + /// Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. + /// + /// The to return a default value for if empty. + /// An that contains default if source is empty; otherwise, source. + public static JsonDocument DefaultIfEmpty(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(queryable.DefaultIfEmpty); + } + + /// + /// Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. + /// + /// The to return a default value for if empty. + /// The value to return if the sequence is empty. + /// An that contains defaultValue if source is empty; otherwise, source. + public static JsonDocument DefaultIfEmpty(this JsonDocument source, object? defaultValue) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.DefaultIfEmpty(defaultValue)); + } + #endregion + + #region Distinct + /// + /// Returns distinct elements from a sequence by using the default equality comparer to compare values. + /// + /// The sequence to remove duplicate elements from. + /// An that contains distinct elements from the source sequence. + public static JsonDocument Distinct(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(queryable.Distinct); + } + #endregion Distinct + #region Select /// /// Projects each element of a sequence into a new form. @@ -352,17 +396,7 @@ private static JsonDocument ToJsonDocumentArray(Func func) var array = new List(); foreach (var dynamicElement in func()) { - object element; - if (dynamicElement is DynamicClass dynamicClass) - { - element = JsonElementUtils.FromObject(dynamicClass); - } - else - { - element = dynamicElement; - } - - //var element = dynamicElement is DynamicClass dynamicClass ? JsonElementUtils.FromObject(dynamicClass) : dynamicElement; + var element = dynamicElement is DynamicClass dynamicClass ? JsonElementUtils.FromObject(dynamicClass) : dynamicElement; array.Add(element); } diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 9a8d0b37..08827214 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -93,6 +93,29 @@ public void Count() result2.Should().Be(1); } + [Fact] + public void Distinct() + { + var json = @"[ + { + ""Name"": ""John"" + }, + { + ""Name"": ""Doe"" + }, + { + ""Name"": ""John"" + } + ]"; + var source = JArray.Parse(json); + + // Act + var result = source.Select("Name").Distinct(); + + // Assert + result.Should().HaveCount(2); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 587e946c..7bd4f267 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -93,6 +93,29 @@ public void Count() result2.Should().Be(1); } + [Fact] + public void Distinct() + { + var json = @"[ + { + ""Name"": ""John"" + }, + { + ""Name"": ""Doe"" + }, + { + ""Name"": ""John"" + } + ]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.Select("Name").Distinct(); + + // Assert + result.RootElement.EnumerateArray().Should().HaveCount(2); + } + [Fact] public void Select() { From e658558972789f7c0b60a797b1ce088d592852ee Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 15:53:01 +0200 Subject: [PATCH 20/53] First --- .../NewtonsoftJsonExtensions.cs | 77 +++++++++++-------- ...em.Linq.Dynamic.Core.NewtonsoftJson.csproj | 5 +- ...em.Linq.Dynamic.Core.SystemTextJson.csproj | 1 + .../SystemTextJsonExtensions.cs | 71 ++++++++++++++++- .../NewtonsoftJsonTests.cs | 9 +++ .../SystemTextJsonTests.cs | 9 +++ 6 files changed, 137 insertions(+), 35 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 0266f2f8..1966187b 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -224,19 +224,6 @@ public static IQueryable Cast(this JArray source, string typeName) #endregion Cast #region Count - /// - /// Returns the number of elements in a sequence. - /// - /// The that contains the elements to be counted. - /// The number of elements in the input sequence. - public static int Count(this JArray source) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Count(); - } - /// /// Returns the number of elements in a sequence. /// @@ -286,44 +273,61 @@ public static int Count(this JArray source, LambdaExpression lambda) /// Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. /// /// The to return a default value for if empty. - /// An that contains default if source is empty; otherwise, source. - public static JArray DefaultIfEmpty(this JArray source) + /// The value to return if the sequence is empty. + /// An that contains defaultValue if source is empty; otherwise, source. + public static JArray DefaultIfEmpty(this JArray source, object? defaultValue) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJArray(queryable.DefaultIfEmpty); + return ToJArray(() => queryable.DefaultIfEmpty(defaultValue)); } + #endregion + #region First /// - /// Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. + /// Returns the first element of a sequence that satisfies a specified condition. /// - /// The to return a default value for if empty. - /// The value to return if the sequence is empty. - /// An that contains defaultValue if source is empty; otherwise, source. - public static JArray DefaultIfEmpty(this JArray source, object? defaultValue) + /// The to return the first element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken First(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); + Check.NotNull(config); - var queryable = ToQueryable(source); - return ToJArray(() => queryable.DefaultIfEmpty(defaultValue)); + var queryable = ToQueryable(source, config); + return ToJToken(queryable.First(config, predicate, args)); } - #endregion - #region Distinct /// - /// Returns distinct elements from a sequence by using the default equality comparer to compare values. + /// Returns the first element of a sequence that satisfies a specified condition. /// - /// The sequence to remove duplicate elements from. - /// An that contains distinct elements from the source sequence. - public static JArray Distinct(this JArray source) + /// The to return the first element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken First(this JArray source, string predicate, params object?[] args) + { + return First(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition. + /// + /// The to return the first element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JToken First(this JArray source, LambdaExpression lambda) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJArray(queryable.Distinct); + return ToJToken(queryable.First(lambda)); } - #endregion Distinct + #endregion First #region Select /// @@ -399,6 +403,16 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi } #endregion Where + #region Private Methods + private static JToken ToJToken(object value) + { + if (value is JToken jToken) + { + return jToken; + } + + return JToken.FromObject(value); + } private static JArray ToJArray(Func func) { var array = new JArray(); @@ -415,4 +429,5 @@ private static IQueryable ToQueryable(JArray source, NewtonsoftJsonParsingConfig { return source.ToDynamicJsonClassArray(config?.DynamicJsonClassOptions).AsQueryable(); } + #endregion } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj index 24d87076..ddf3dbde 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj @@ -8,7 +8,7 @@ TODO system;linq;dynamic;core;dotnet;json {8C5851B8-5C47-4229-AB55-D4252703598E} - net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 + net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 1.3.$(PatchVersion) @@ -22,7 +22,8 @@ - + + diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj index f5c089ea..41fac28e 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -29,6 +29,7 @@ + diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index a97ca545..b0bb8965 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -326,6 +326,64 @@ public static JsonDocument Distinct(this JsonDocument source) } #endregion Distinct + #region First + /// + /// Returns the first element of a sequence. + /// + /// The to return the first element of. + /// The first element in source. + public static JsonElement First(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.First()); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition. + /// + /// The to return the first element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement First(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJsonElement(queryable.First(config, predicate, args)); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition. + /// + /// The to return the first element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement First(this JsonDocument source, string predicate, params object?[] args) + { + return First(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition. + /// + /// The to return the first element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JsonElement First(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.First(lambda)); + } + #endregion First + #region Select /// /// Projects each element of a sequence into a new form. @@ -391,13 +449,22 @@ public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsing #endregion Where #region Private Methods + private static JsonElement ToJsonElement(object value) + { + if (value is JsonElement jsonElement) + { + return jsonElement; + } + + return JsonElementUtils.FromObject(value); + } + private static JsonDocument ToJsonDocumentArray(Func func) { var array = new List(); foreach (var dynamicElement in func()) { - var element = dynamicElement is DynamicClass dynamicClass ? JsonElementUtils.FromObject(dynamicClass) : dynamicElement; - array.Add(element); + array.Add(ToJsonElement(dynamicElement)); } return JsonDocumentUtils.FromObject(array); diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 08827214..1911f677 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -116,6 +116,15 @@ public void Distinct() result.Should().HaveCount(2); } + [Fact] + public void First() + { + // Act + Assert 1 + _source.First().Should().NotBeNull(); + + ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 7bd4f267..0b761b87 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -116,6 +116,15 @@ public void Distinct() result.RootElement.EnumerateArray().Should().HaveCount(2); } + [Fact] + public void First() + { + // Act + Assert 1 + _source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText()); + + _source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + } + [Fact] public void Select() { From 1c25fe22323521002c1345bab90642a49f71139f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 16:02:13 +0200 Subject: [PATCH 21/53] FirstOrDefault --- .../NewtonsoftJsonExtensions.cs | 52 +++++++++++++- .../SystemTextJsonExtensions.cs | 67 ++++++++++++++++++- .../NewtonsoftJsonTests.cs | 14 ++++ .../SystemTextJsonTests.cs | 14 ++++ 4 files changed, 144 insertions(+), 3 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 1966187b..50f117cd 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -329,6 +329,51 @@ public static JToken First(this JArray source, LambdaExpression lambda) } #endregion First + #region FirstOrDefault + /// + /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. + /// + /// The to return the first element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + public static JToken? FirstOrDefault(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJToken(queryable.FirstOrDefault(predicate, args)); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. + /// + /// The to return the first element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + public static JToken? FirstOrDefault(this JArray source, string predicate, params object?[] args) + { + return FirstOrDefault(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. + /// + /// The to return the first element of. + /// A cached Lambda Expression. + /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + public static JToken? FirstOrDefault(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.FirstOrDefault(lambda)); + } + #endregion FirstOrDefault + #region Select /// /// Projects each element of a sequence into a new form. @@ -404,8 +449,13 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi #endregion Where #region Private Methods - private static JToken ToJToken(object value) + private static JToken? ToJToken(object? value) { + if (value == null) + { + return null; + } + if (value is JToken jToken) { return jToken; diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index b0bb8965..55b6b068 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -384,6 +384,64 @@ public static JsonElement First(this JsonDocument source, LambdaExpression lambd } #endregion First + #region FirstOrDefault + /// + /// Returns the first element of a sequence, or a default value if the sequence contains no elements. + /// + /// The to return the first element of. + /// default if source is empty; otherwise, the first element in source. + public static JsonElement? FirstOrDefault(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.FirstOrDefault()); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. + /// + /// The to return the first element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + public static JsonElement? FirstOrDefault(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.FirstOrDefault(predicate, args)); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. + /// + /// The to return the first element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + public static JsonElement? FirstOrDefault(this JsonDocument source, string predicate, params object?[] args) + { + return FirstOrDefault(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. + /// + /// The to return the first element of. + /// A cached Lambda Expression. + /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + public static JsonElement? FirstOrDefault(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.FirstOrDefault(lambda)); + } + #endregion FirstOrDefault + #region Select /// /// Projects each element of a sequence into a new form. @@ -449,8 +507,13 @@ public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsing #endregion Where #region Private Methods - private static JsonElement ToJsonElement(object value) + private static JsonElement? ToJsonElement(object? value) { + if (value == null) + { + return null; + } + if (value is JsonElement jsonElement) { return jsonElement; @@ -461,7 +524,7 @@ private static JsonElement ToJsonElement(object value) private static JsonDocument ToJsonDocumentArray(Func func) { - var array = new List(); + var array = new List(); foreach (var dynamicElement in func()) { array.Add(ToJsonElement(dynamicElement)); diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 1911f677..208895ec 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -122,9 +122,23 @@ public void First() // Act + Assert 1 _source.First().Should().NotBeNull(); + // Act + Assert 2 ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); } + [Fact] + public void FirstOrDefault() + { + // Act + Assert 1 + _source.FirstOrDefault().Should().NotBeNull(); + + // Act + Assert 2 + ((string?)_source.FirstOrDefault("Age > 30")!["Name"]).Should().Be("Doe"); + + // Act + Assert 3 + _source.FirstOrDefault("Age > 999").Should().BeNull(); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 0b761b87..f8c1e43f 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -122,9 +122,23 @@ public void First() // Act + Assert 1 _source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText()); + // Act + Assert 2 _source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); } + [Fact] + public void FirstOrDefault() + { + // Act + Assert 1 + _source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText()); + + // Act + Assert 2 + _source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + + // Act + Assert 3 + _source.FirstOrDefault("Age > 999").Should().BeNull(); + } + [Fact] public void Select() { From bf2e7fa9bf3e28847d6a4b0572f3572f540a348e Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 17:48:38 +0200 Subject: [PATCH 22/53] Last --- .../NewtonsoftJsonExtensions.cs | 45 ++++++++++++++ .../SystemTextJsonExtensions.cs | 58 +++++++++++++++++++ .../NewtonsoftJsonTests.cs | 17 +++--- .../SystemTextJsonTests.cs | 10 ++++ 4 files changed, 122 insertions(+), 8 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 50f117cd..83383823 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -374,6 +374,51 @@ public static JToken First(this JArray source, LambdaExpression lambda) } #endregion FirstOrDefault + #region Last + /// + /// Returns the last element of a sequence that satisfies a specified condition. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken Last(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJToken(queryable.Last(predicate, args)); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken Last(this JArray source, string predicate, params object?[] args) + { + return Last(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JToken Last(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.Last(lambda)); + } + #endregion Last + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 55b6b068..58b08acb 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -442,6 +442,64 @@ public static JsonElement First(this JsonDocument source, LambdaExpression lambd } #endregion FirstOrDefault + #region Last + /// + /// Returns the last element of a sequence. + /// + /// The to return the last element of. + /// The last element in source. + public static JsonElement Last(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Last()); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement Last(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Last(predicate, args)); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement Last(this JsonDocument source, string predicate, params object?[] args) + { + return Last(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JsonElement Last(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Last(lambda)); + } + #endregion Last + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 208895ec..5815604f 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -119,10 +119,7 @@ public void Distinct() [Fact] public void First() { - // Act + Assert 1 - _source.First().Should().NotBeNull(); - - // Act + Assert 2 + // Act + Assert ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); } @@ -130,15 +127,19 @@ public void First() public void FirstOrDefault() { // Act + Assert 1 - _source.FirstOrDefault().Should().NotBeNull(); - - // Act + Assert 2 ((string?)_source.FirstOrDefault("Age > 30")!["Name"]).Should().Be("Doe"); - // Act + Assert 3 + // Act + Assert 2 _source.FirstOrDefault("Age > 999").Should().BeNull(); } + [Fact] + public void Last() + { + // Act + Assert + ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index f8c1e43f..dd0f53ce 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -139,6 +139,16 @@ public void FirstOrDefault() _source.FirstOrDefault("Age > 999").Should().BeNull(); } + [Fact] + public void Last() + { + // Act + Assert 1 + _source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + + // Act + Assert 2 + _source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + } + [Fact] public void Select() { From 9f074d02744b3ed8bcce42009aa2dfcbdc160e9a Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 21:17:37 +0200 Subject: [PATCH 23/53] LastOrDefault --- .../NewtonsoftJsonExtensions.cs | 45 ++++++++++++++ .../SystemTextJsonExtensions.cs | 58 +++++++++++++++++++ .../NewtonsoftJsonTests.cs | 10 ++++ .../SystemTextJsonTests.cs | 13 +++++ 4 files changed, 126 insertions(+) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 83383823..c4e2b7ed 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -419,6 +419,51 @@ public static JToken Last(this JArray source, LambdaExpression lambda) } #endregion Last + #region LastOrDefault + /// + /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken? LastOrDefault(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJToken(queryable.LastOrDefault(predicate, args)); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken? LastOrDefault(this JArray source, string predicate, params object?[] args) + { + return LastOrDefault(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JToken? LastOrDefault(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.LastOrDefault(lambda)); + } + #endregion LastOrDefault + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 58b08acb..894f50b6 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -500,6 +500,64 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda } #endregion Last + #region LastOrDefault + /// + /// Returns the last element of a sequence, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// default if source is empty; otherwise, the last element in source. + public static JsonElement? LastOrDefault(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.LastOrDefault()); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement? LastOrDefault(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.LastOrDefault(predicate, args)); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement? LastOrDefault(this JsonDocument source, string predicate, params object?[] args) + { + return LastOrDefault(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JsonElement? LastOrDefault(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.LastOrDefault(lambda)); + } + #endregion LastOrDefault + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 5815604f..b1ede0fa 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -140,6 +140,16 @@ public void Last() ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); } + [Fact] + public void LastOrDefault() + { + // Act + Assert 1 + ((string?)_source.LastOrDefault("Age > 0")!["Name"]).Should().Be("Doe"); + + // Act + Assert 2 + _source.LastOrDefault("Age > 999").Should().BeNull(); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index dd0f53ce..0d988b21 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -149,6 +149,19 @@ public void Last() _source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); } + [Fact] + public void LastOrDefault() + { + // Act + Assert 1 + _source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + + // Act + Assert 2 + _source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + + // Act + Assert 3 + _source.LastOrDefault("Age > 999").Should().BeNull(); + } + [Fact] public void Select() { From aa607515663f812eeadedaae2cf5553f284476dd Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 21:35:09 +0200 Subject: [PATCH 24/53] Max / Min --- .../NewtonsoftJsonExtensions.cs | 90 +++++++++++++ .../SystemTextJsonExtensions.cs | 118 +++++++++++++++++- .../NewtonsoftJsonTests.cs | 14 +++ .../SystemTextJsonTests.cs | 14 +++ 4 files changed, 235 insertions(+), 1 deletion(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index c4e2b7ed..dca64b00 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -464,6 +464,96 @@ public static JToken Last(this JArray source, LambdaExpression lambda) } #endregion LastOrDefault + #region Max + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The max element in the sequence. + public static JToken Max(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJToken(queryable.Max(config, predicate, args))!; + } + + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The max element in the sequence. + public static JToken Max(this JArray source, string predicate, params object?[] args) + { + return Max(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// A Lambda Expression. + /// The max element in the sequence. + public static JToken Max(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.Max(lambda))!; + } + #endregion Max + + #region Min + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The min element in the sequence. + public static JToken Min(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJToken(queryable.Min(config, predicate, args))!; + } + + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The min element in the sequence. + public static JToken Min(this JArray source, string predicate, params object?[] args) + { + return Min(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// A Lambda Expression. + /// The min element in the sequence. + public static JToken Min(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.Min(lambda))!; + } + #endregion Min + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 894f50b6..e17059b2 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -528,7 +528,7 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda Check.NotNull(config); var queryable = ToQueryable(source); - return ToJsonElement(queryable.LastOrDefault(predicate, args)); + return ToJsonElement(queryable.LastOrDefault(config, predicate, args)); } /// @@ -558,6 +558,122 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda } #endregion LastOrDefault + #region Max + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// The max element in the sequence. + public static JsonElement Max(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Max()) ?? default; + } + + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The max element in the sequence. + public static JsonElement Max(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Max(config, predicate, args)) ?? default; + } + + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The max element in the sequence. + public static JsonElement Max(this JsonDocument source, string predicate, params object?[] args) + { + return Max(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the max element of a sequence. + /// + /// A sequence of values to calculate find the max for. + /// A Lambda Expression. + /// The max element in the sequence. + public static JsonElement Max(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Max(lambda)) ?? default; + } + #endregion Max + + #region Min + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// The min element in the sequence. + public static JsonElement Min(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Min()) ?? default; + } + + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The min element in the sequence. + public static JsonElement Min(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Min(config, predicate, args)) ?? default; + } + + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The min element in the sequence. + public static JsonElement Min(this JsonDocument source, string predicate, params object?[] args) + { + return Min(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the min element of a sequence. + /// + /// A sequence of values to calculate find the min for. + /// A Lambda Expression. + /// The min element in the sequence. + public static JsonElement Min(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Min(lambda)) ?? default; + } + #endregion Min + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index b1ede0fa..61a1c4b4 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -150,6 +150,20 @@ public void LastOrDefault() _source.LastOrDefault("Age > 999").Should().BeNull(); } + [Fact] + public void Max() + { + // Act + Assert + ((string?)_source.Max("Age")).Should().Be("40"); + } + + [Fact] + public void Min() + { + // Act + Assert + ((string?)_source.Min("Age")).Should().Be("30"); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 0d988b21..d4cf6bd5 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -162,6 +162,20 @@ public void LastOrDefault() _source.LastOrDefault("Age > 999").Should().BeNull(); } + [Fact] + public void Max() + { + // Act + Assert + _source.Max("Age").GetRawText().Should().BeEquivalentTo("40"); + } + + [Fact] + public void Min() + { + // Act + Assert + _source.Min("Age").GetRawText().Should().BeEquivalentTo("30"); + } + [Fact] public void Select() { From d56b0d375ac7a0e216a20101178a0a78f3448838 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Apr 2024 23:06:12 +0200 Subject: [PATCH 25/53] OrderBy --- .../NewtonsoftJsonExtensions.cs | 75 +++++++++++++++-- .../Config/SystemTextJsonParsingConfig.cs | 2 +- .../SystemTextJsonExtensions.cs | 80 ++++++++++++++++--- .../DynamicQueryableExtensions.cs | 31 ++++--- .../NewtonsoftJsonTests.cs | 18 +++++ .../SystemTextJsonTests.cs | 18 +++++ 6 files changed, 191 insertions(+), 33 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index dca64b00..bd3c97b3 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -1,4 +1,5 @@ -using System.Linq.Dynamic.Core.NewtonsoftJson.Config; +using System.Collections; +using System.Linq.Dynamic.Core.NewtonsoftJson.Config; using System.Linq.Dynamic.Core.NewtonsoftJson.Extensions; using System.Linq.Dynamic.Core.Validation; using System.Linq.Expressions; @@ -237,7 +238,7 @@ public static int Count(this JArray source, NewtonsoftJsonParsingConfig config, Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return queryable.Count(config, predicate, args); } @@ -343,7 +344,7 @@ public static JToken First(this JArray source, LambdaExpression lambda) Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJToken(queryable.FirstOrDefault(predicate, args)); } @@ -388,7 +389,7 @@ public static JToken Last(this JArray source, NewtonsoftJsonParsingConfig config Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJToken(queryable.Last(predicate, args)); } @@ -433,7 +434,7 @@ public static JToken Last(this JArray source, LambdaExpression lambda) Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJToken(queryable.LastOrDefault(predicate, args)); } @@ -478,7 +479,7 @@ public static JToken Max(this JArray source, NewtonsoftJsonParsingConfig config, Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJToken(queryable.Max(config, predicate, args))!; } @@ -523,7 +524,7 @@ public static JToken Min(this JArray source, NewtonsoftJsonParsingConfig config, Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJToken(queryable.Min(config, predicate, args))!; } @@ -554,6 +555,66 @@ public static JToken Min(this JArray source, LambdaExpression lambda) } #endregion Min + #region OrderBy + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray OrderBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(source); + Check.NotNullOrEmpty(ordering); + + var queryable = ToQueryable(source, config); + return ToJArray(() => queryable.OrderBy(config, ordering, args)); + } + + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray OrderBy(this JArray source, string ordering, params object?[] args) + { + return OrderBy(source, NewtonsoftJsonParsingConfig.Default, ordering, args); + } + + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray OrderBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) + { + var queryable = ToQueryable(source, config); + return ToJArray(() => queryable.OrderBy(config, ordering, comparer, args)); + } + + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray OrderBy(this JArray source, string ordering, IComparer comparer, params object?[] args) + { + return OrderBy(source, NewtonsoftJsonParsingConfig.Default, ordering, comparer, args); + } + #endregion OrderBy + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs index c52c5078..17c6fcb0 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs @@ -6,5 +6,5 @@ public class SystemTextJsonParsingConfig : ParsingConfig { public static SystemTextJsonParsingConfig Default { get; } = new(); - public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } + // public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index e17059b2..c88b590e 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections; +using System.Collections.Generic; using System.Linq.Dynamic.Core.SystemTextJson.Config; using System.Linq.Dynamic.Core.SystemTextJson.Extensions; using System.Linq.Dynamic.Core.SystemTextJson.Utils; @@ -149,7 +150,7 @@ public static double Average(this JsonDocument source, SystemTextJsonParsingConf Check.NotNull(config); Check.NotEmpty(predicate); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return queryable.Average(config, predicate, args); } @@ -251,7 +252,7 @@ public static int Count(this JsonDocument source, SystemTextJsonParsingConfig co Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return queryable.Count(config, predicate, args); } @@ -411,7 +412,7 @@ public static JsonElement First(this JsonDocument source, LambdaExpression lambd Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJsonElement(queryable.FirstOrDefault(predicate, args)); } @@ -469,7 +470,7 @@ public static JsonElement Last(this JsonDocument source, SystemTextJsonParsingCo Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJsonElement(queryable.Last(predicate, args)); } @@ -527,7 +528,7 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJsonElement(queryable.LastOrDefault(config, predicate, args)); } @@ -585,7 +586,7 @@ public static JsonElement Max(this JsonDocument source, SystemTextJsonParsingCon Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJsonElement(queryable.Max(config, predicate, args)) ?? default; } @@ -643,7 +644,7 @@ public static JsonElement Min(this JsonDocument source, SystemTextJsonParsingCon Check.NotNull(source); Check.NotNull(config); - var queryable = ToQueryable(source); + var queryable = ToQueryable(source, config); return ToJsonElement(queryable.Min(config, predicate, args)) ?? default; } @@ -674,6 +675,66 @@ public static JsonElement Min(this JsonDocument source, LambdaExpression lambda) } #endregion Min + #region OrderBy + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument OrderBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(source); + Check.NotNullOrEmpty(ordering); + + var queryable = ToQueryable(source, config); + return ToJsonDocumentArray(() => queryable.OrderBy(config, ordering, args)); + } + + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument OrderBy(this JsonDocument source, string ordering, params object?[] args) + { + return OrderBy(source, SystemTextJsonParsingConfig.Default, ordering, args); + } + + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument OrderBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) + { + var queryable = ToQueryable(source, config); + return ToJsonDocumentArray(() => queryable.OrderBy(config, ordering, comparer, args)); + } + + /// + /// Sorts the elements of a sequence in ascending or descending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument OrderBy(this JsonDocument source, string ordering, IComparer comparer, params object?[] args) + { + return OrderBy(source, SystemTextJsonParsingConfig.Default, ordering, comparer, args); + } + #endregion OrderBy + #region Select /// /// Projects each element of a sequence into a new form. @@ -765,6 +826,7 @@ private static JsonDocument ToJsonDocumentArray(Func func) return JsonDocumentUtils.FromObject(array); } + // ReSharper disable once UnusedParameter.Local private static IQueryable ToQueryable(JsonDocument source, SystemTextJsonParsingConfig? config = null) { var array = source.RootElement; @@ -773,7 +835,7 @@ private static IQueryable ToQueryable(JsonDocument source, SystemTextJsonParsing throw new NotSupportedException("The source is not a JSON array."); } - return JsonDocumentExtensions.ToDynamicJsonClassArray(array, config?.DynamicJsonClassOptions).AsQueryable(); + return JsonDocumentExtensions.ToDynamicJsonClassArray(array).AsQueryable(); } #endregion } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index fc126865..47cf5dad 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -1565,11 +1565,23 @@ public static IOrderedQueryable OrderBy(this IQueryable source, ParsingConfig co return InternalOrderBy(source, config, ordering, comparer, args); } + /// + public static IOrderedQueryable OrderBy(this IQueryable source, string ordering, params object?[] args) + { + return OrderBy(source, ParsingConfig.Default, ordering, args); + } + + /// + public static IOrderedQueryable OrderBy(this IQueryable source, string ordering, IComparer comparer, params object?[] args) + { + return OrderBy(source, ParsingConfig.Default, ordering, comparer, args); + } + internal static IOrderedQueryable InternalOrderBy(IQueryable source, ParsingConfig config, string ordering, object? comparer, params object?[] args) { Check.NotNull(source); Check.NotNull(config); - Check.NotEmpty(ordering, nameof(ordering)); + Check.NotEmpty(ordering); ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty, config.RenameEmptyParameterExpressionNames) }; ExpressionParser parser = new ExpressionParser(parameters, ordering, args, config); @@ -1616,19 +1628,6 @@ internal static IOrderedQueryable InternalOrderBy(IQueryable source, ParsingConf var optimized = OptimizeExpression(queryExpr); return (IOrderedQueryable)source.Provider.CreateQuery(optimized); } - - /// - public static IOrderedQueryable OrderBy(this IQueryable source, string ordering, params object?[] args) - { - return OrderBy(source, ParsingConfig.Default, ordering, args); - } - - /// - public static IOrderedQueryable OrderBy(this IQueryable source, string ordering, IComparer comparer, params object?[] args) - { - return OrderBy(source, ParsingConfig.Default, ordering, comparer, args); - } - #endregion OrderBy #region Page/PageResult @@ -1803,10 +1802,10 @@ public static IQueryable Select(this IQueryable source, Parsin LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, typeof(TResult), selector, args); var methodCallExpression = Expression.Call( - typeof(Queryable), + typeof(Queryable), nameof(Queryable.Select), new[] { source.ElementType, typeof(TResult) }, - source.Expression, + source.Expression, Expression.Quote(lambda) ); diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 61a1c4b4..488fb5b5 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -164,6 +164,24 @@ public void Min() ((string?)_source.Min("Age")).Should().Be("30"); } + [Fact] + public void OrderBy() + { + // Act 1 + var result = _source.OrderBy("Age").Select("Name"); + + // Assert 1 + var array = result.Select(x => x.Value()); + array.Should().BeEquivalentTo("John", "Doe"); + + // Act 1 + var resultAsc = _source.OrderBy("Age", "Asc").Select("Name"); + + // Assert 1 + var arrayAsc = resultAsc.Select(x => x.Value()); + arrayAsc.Should().BeEquivalentTo("Doe", "John"); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index d4cf6bd5..0d7e0b3e 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -176,6 +176,24 @@ public void Min() _source.Min("Age").GetRawText().Should().BeEquivalentTo("30"); } + [Fact] + public void OrderBy() + { + // Act 1 + var result = _source.OrderBy("Age").Select("Name"); + + // Assert 1 + var array = result.RootElement.EnumerateArray().Select(x => x.GetString()); + array.Should().BeEquivalentTo("John", "Doe"); + + // Act 1 + var resultAsc = _source.OrderBy("Age", "Asc").Select("Name"); + + // Assert 1 + var arrayAsc = resultAsc.RootElement.EnumerateArray().Select(x => x.GetString()); + arrayAsc.Should().BeEquivalentTo("Doe", "John"); + } + [Fact] public void Select() { From 411047020976af92e5a747d7e34155c34f1bdc50 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 10:12:48 +0200 Subject: [PATCH 26/53] Page --- .../NewtonsoftJsonExtensions.cs | 33 ++++++ .../Config/SystemTextJsonParsingConfig.cs | 6 +- .../SystemTextJsonExtensions.cs | 48 ++++++++ src/System.Linq.Dynamic.Core/PagedResult.cs | 103 +++++++++--------- .../NewtonsoftJsonTests.cs | 34 ++++++ .../SystemTextJsonTests.cs | 34 ++++++ 6 files changed, 201 insertions(+), 57 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index bd3c97b3..18bc8216 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -615,6 +615,39 @@ public static JArray OrderBy(this JArray source, string ordering, IComparer comp } #endregion OrderBy + #region Page/PageResult + /// + /// Returns the elements as paged. + /// + /// The source + /// The page to return. + /// The number of elements per page. + /// A that contains the paged elements. + public static JArray Page(this JArray source, int page, int pageSize) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJArray(() => queryable.Page(page, pageSize)); + } + + /// + /// Returns the elements as paged and include the CurrentPage, PageCount, PageSize and RowCount. + /// + /// The source + /// The page to return. + /// The number of elements per page. + /// If this optional parameter has been defined, this value is used as the RowCount instead of executing a Linq `Count()`. + /// PagedResult + public static PagedResult PageResult(this JArray source, int page, int pageSize, int? rowCount = null) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.PageResult(page, pageSize, rowCount); + } + #endregion Page/PageResult + #region Select /// /// Projects each element of a sequence into a new form. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs index 17c6fcb0..d5ada435 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs @@ -1,10 +1,6 @@ -using JsonConverter.Abstractions.Models; - -namespace System.Linq.Dynamic.Core.SystemTextJson.Config; +namespace System.Linq.Dynamic.Core.SystemTextJson.Config; public class SystemTextJsonParsingConfig : ParsingConfig { public static SystemTextJsonParsingConfig Default { get; } = new(); - - // public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index c88b590e..b7605943 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -767,6 +767,54 @@ public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsin } #endregion Select + #region Page/PageResult + /// + /// Returns the elements as paged. + /// + /// The source + /// The page to return. + /// The number of elements per page. + /// A that contains the paged elements. + public static JsonDocument Page(this JsonDocument source, int page, int pageSize) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.Page(page, pageSize)); + } + + /// + /// Returns the elements as paged and include the CurrentPage, PageCount, PageSize and RowCount. + /// + /// The source + /// The page to return. + /// The number of elements per page. + /// If this optional parameter has been defined, this value is used as the RowCount instead of executing a Linq `Count()`. + /// PagedResult + public static PagedResult PageResult(this JsonDocument source, int page, int pageSize, int? rowCount = null) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.PageResult(page, pageSize, rowCount); + } + #endregion Page/PageResult + + #region Reverse + /// + /// Inverts the order of the elements in a sequence. + /// + /// The source + /// A whose elements correspond to those of the input sequence in reverse order. + public static JsonDocument Reverse(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.Reverse()); + } + #endregion Reverse + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core/PagedResult.cs b/src/System.Linq.Dynamic.Core/PagedResult.cs index 6d175bed..cb7f5813 100644 --- a/src/System.Linq.Dynamic.Core/PagedResult.cs +++ b/src/System.Linq.Dynamic.Core/PagedResult.cs @@ -1,64 +1,63 @@  -namespace System.Linq.Dynamic.Core +namespace System.Linq.Dynamic.Core; + +/// +/// PagedResult +/// +public class PagedResult { /// - /// PagedResult + /// Gets or sets the queryable. /// - public class PagedResult - { - /// - /// Gets or sets the queryable. - /// - /// - /// The queryable. - /// - public IQueryable Queryable { get; set; } + /// + /// The queryable. + /// + public IQueryable Queryable { get; set; } = null!; - /// - /// Gets or sets the current page. - /// - /// - /// The current page. - /// - public int CurrentPage { get; set; } + /// + /// Gets or sets the current page. + /// + /// + /// The current page. + /// + public int CurrentPage { get; set; } - /// - /// Gets or sets the page count. - /// - /// - /// The page count. - /// - public int PageCount { get; set; } + /// + /// Gets or sets the page count. + /// + /// + /// The page count. + /// + public int PageCount { get; set; } - /// - /// Gets or sets the size of the page. - /// - /// - /// The size of the page. - /// - public int PageSize { get; set; } + /// + /// Gets or sets the size of the page. + /// + /// + /// The size of the page. + /// + public int PageSize { get; set; } - /// - /// Gets or sets the row count. - /// - /// - /// The row count. - /// - public int RowCount { get; set; } - } + /// + /// Gets or sets the row count. + /// + /// + /// The row count. + /// + public int RowCount { get; set; } +} +/// +/// PagedResult{TSource} +/// +/// The type of the source. +public class PagedResult : PagedResult +{ /// - /// PagedResult{TSource} + /// Gets or sets the queryable. /// - /// The type of the source. - public class PagedResult : PagedResult - { - /// - /// Gets or sets the queryable. - /// - /// - /// The queryable. - /// - public new IQueryable Queryable { get; set; } - } + /// + /// The queryable. + /// + public new IQueryable Queryable { get; set; } = null!; } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 488fb5b5..04886be6 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -182,6 +182,40 @@ public void OrderBy() arrayAsc.Should().BeEquivalentTo("Doe", "John"); } + [Fact] + public void Page() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JArray.Parse(json); + + // Act + var result = source.Page(2, 3); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().ContainInOrder(4, 5, 6); + } + + [Fact] + public void PageResult() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JArray.Parse(json); + + // Act + var pagedResult = source.PageResult(2, 3); + + // Assert + pagedResult.Should().BeEquivalentTo(new PagedResult + { + CurrentPage = 2, + PageCount = 4, + PageSize = 3, + RowCount = 10, + Queryable = new[] { 4, 5, 6 }.AsQueryable() + }); + } + [Fact] public void Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 0d7e0b3e..a6eb952d 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -194,6 +194,40 @@ public void OrderBy() arrayAsc.Should().BeEquivalentTo("Doe", "John"); } + [Fact] + public void Page() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.Page(2, 3); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetInt32()); + array.Should().ContainInOrder(4, 5, 6); + } + + [Fact] + public void PageResult() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JsonDocument.Parse(json); + + // Act + var pagedResult = source.PageResult(2, 3); + + // Assert + pagedResult.Should().BeEquivalentTo(new PagedResult + { + CurrentPage = 2, + PageCount = 4, + PageSize = 3, + RowCount = 10, + Queryable = new[] { 4, 5, 6 }.AsQueryable() + }); + } + [Fact] public void Select() { From ab3f62790ff2bc260832bdeaa92b674c62125e6b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 10:13:05 +0200 Subject: [PATCH 27/53] . --- .../NewtonsoftJsonTests.cs | 18 +++++++++--------- .../SystemTextJsonTests.cs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 04886be6..6cad3b24 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -7,15 +7,15 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests; public class NewtonsoftJsonTests { private const string ExampleJson = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - } - ]"; + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 40 + } + ]"; private readonly JArray _source = JArray.Parse(ExampleJson); [Fact] diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index a6eb952d..dd535e6e 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -7,15 +7,15 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; public class SystemTextJsonTests { private const string ExampleJson = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - } - ]"; + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 40 + } + ]"; private readonly JsonDocument _source = JsonDocument.Parse(ExampleJson); [Fact] From 680c879af5af5b15d22c38755c92af11d5d9f067 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 10:39:03 +0200 Subject: [PATCH 28/53] Select_ResultType --- .../NewtonsoftJsonExtensions.cs | 33 +++++++ .../SystemTextJsonExtensions.cs | 97 +++++++++++++------ .../DynamicQueryableExtensions.cs | 11 +-- .../NewtonsoftJsonTests.cs | 15 +++ .../SystemTextJsonTests.cs | 18 +++- 5 files changed, 135 insertions(+), 39 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 18bc8216..248f09a4 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -683,6 +683,39 @@ public static JArray Select(this JArray source, NewtonsoftJsonParsingConfig conf var queryable = ToQueryable(source, config); return ToJArray(() => queryable.Select(config, selector, args)); } + + /// + /// Projects each element of a sequence into a new class of type TResult. + /// Details see http://solutionizing.net/category/linq/ + /// + /// The source + /// The . + /// The result type. + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JArray Select(this JArray source, NewtonsoftJsonParsingConfig config, Type resultType, string selector, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJArray(() => queryable.Select(config, resultType, selector, args)); + } + + /// + /// Projects each element of a sequence into a new class of type TResult. + /// Details see http://solutionizing.net/category/linq/ + /// + /// The source + /// The result type. + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JArray Select(this JArray source, Type resultType, string selector, params object?[] args) + { + return Select(source, NewtonsoftJsonParsingConfig.Default, resultType, selector, args); + } #endregion Select #region Where diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index b7605943..1c572007 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -735,38 +735,6 @@ public static JsonDocument OrderBy(this JsonDocument source, string ordering, IC } #endregion OrderBy - #region Select - /// - /// Projects each element of a sequence into a new form. - /// - /// The source - /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. - /// An whose elements are the result of invoking a projection string on each element of source. - public static JsonDocument Select(this JsonDocument source, string selector, params object?[] args) - { - return Select(source, SystemTextJsonParsingConfig.Default, selector, args); - } - - /// - /// Projects each element of a sequence into a new form. - /// - /// The source - /// The . - /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. - /// An whose elements are the result of invoking a projection string on each element of source. - public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args) - { - Check.NotNull(source); - Check.NotNull(config); - Check.NotNullOrEmpty(selector); - - var queryable = ToQueryable(source, config); - return ToJsonDocumentArray(() => queryable.Select(config, selector, args)); - } - #endregion Select - #region Page/PageResult /// /// Returns the elements as paged. @@ -815,6 +783,71 @@ public static JsonDocument Reverse(this JsonDocument source) } #endregion Reverse + #region Select + /// + /// Projects each element of a sequence into a new form. + /// + /// The source + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JsonDocument Select(this JsonDocument source, string selector, params object?[] args) + { + return Select(source, SystemTextJsonParsingConfig.Default, selector, args); + } + + /// + /// Projects each element of a sequence into a new form. + /// + /// The source + /// The . + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + Check.NotNullOrEmpty(selector); + + var queryable = ToQueryable(source, config); + return ToJsonDocumentArray(() => queryable.Select(config, selector, args)); + } + + /// + /// Projects each element of a sequence into a new class of type TResult. + /// Details see http://solutionizing.net/category/linq/ + /// + /// The source + /// The . + /// The result type. + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, Type resultType, string selector, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJsonDocumentArray(() => queryable.Select(config, resultType, selector, args)); + } + + /// + /// Projects each element of a sequence into a new class of type TResult. + /// Details see http://solutionizing.net/category/linq/ + /// + /// The source + /// The result type. + /// A projection string expression to apply to each element. + /// An object array that contains zero or more objects to insert into the predicate as parameters. + /// An whose elements are the result of invoking a projection string on each element of source. + public static JsonDocument Select(this JsonDocument source, Type resultType, string selector, params object?[] args) + { + return Select(source, SystemTextJsonParsingConfig.Default, resultType, selector, args); + } + #endregion Select + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 47cf5dad..116ff6cb 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -1755,7 +1755,7 @@ public static IQueryable Select(this IQueryable source, ParsingConfig config, st { Check.NotNull(source); Check.NotNull(config); - Check.NotEmpty(selector, nameof(selector)); + Check.NotEmpty(selector); bool createParameterCtor = config.EvaluateGroupByAtDatabase || SupportsLinqToObjects(config, source); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, selector, args); @@ -1796,7 +1796,7 @@ public static IQueryable Select(this IQueryable source, Parsin { Check.NotNull(source); Check.NotNull(config); - Check.NotEmpty(selector, nameof(selector)); + Check.NotEmpty(selector); bool createParameterCtor = config.EvaluateGroupByAtDatabase || SupportsLinqToObjects(config, source); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, typeof(TResult), selector, args); @@ -1840,7 +1840,7 @@ public static IQueryable Select(this IQueryable source, ParsingConfig config, Ty Check.NotNull(source); Check.NotNull(config); Check.NotNull(resultType, nameof(resultType)); - Check.NotEmpty(selector, nameof(selector)); + Check.NotEmpty(selector); bool createParameterCtor = config.EvaluateGroupByAtDatabase || SupportsLinqToObjects(config, source); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, resultType, selector, args); @@ -1858,7 +1858,6 @@ public static IQueryable Select(this IQueryable source, Type resultType, string { return Select(source, ParsingConfig.Default, resultType, selector, args); } - #endregion Select #region SelectMany @@ -1905,7 +1904,7 @@ public static IQueryable SelectMany(this IQueryable source, ParsingConfig config Check.NotNull(source); Check.NotNull(config); Check.NotNull(resultType, nameof(resultType)); - Check.NotEmpty(selector, nameof(selector)); + Check.NotEmpty(selector); return SelectManyInternal(source, config, resultType, selector, args); } @@ -1976,7 +1975,7 @@ public static IQueryable SelectMany(this IQueryable source, Pa { Check.NotNull(source); Check.NotNull(config); - Check.NotEmpty(selector, nameof(selector)); + Check.NotEmpty(selector); bool createParameterCtor = config.EvaluateGroupByAtDatabase || SupportsLinqToObjects(config, source); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, selector, args); diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 6cad3b24..842dd0ff 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -227,6 +227,21 @@ public void Select() array.Should().BeEquivalentTo("John", "Doe"); } + [Fact] + public void Select_ResultType() + { + // Arrange + var json = @"[1, 2, 3]"; + var source = JArray.Parse(json); + + // Act + var result = source.Select(typeof(int), "it * it"); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().ContainInOrder(1, 4, 9); + } + [Fact] public void Where_Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index dd535e6e..37888bc2 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.IO; +using System.Text.Json; using FluentAssertions; using Xunit; @@ -239,6 +240,21 @@ public void Select() array.Should().BeEquivalentTo("John", "Doe"); } + [Fact] + public void Select_ResultType() + { + // Arrange + var json = @"[1, 2, 3]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.Select(typeof(int), "it * it"); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetInt32()); + array.Should().ContainInOrder(1, 4, 9); + } + [Fact] public void Where_Select() { From 8677ab526084c697fbf453aa4245fcab3a0ed1e9 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 10:56:57 +0200 Subject: [PATCH 29/53] Single / SingleOrDefault --- .../NewtonsoftJsonExtensions.cs | 109 ++++++++++++++++ .../SystemTextJsonExtensions.cs | 123 ++++++++++++++++++ .../NewtonsoftJsonTests.cs | 17 +++ .../SystemTextJsonTests.cs | 14 ++ 4 files changed, 263 insertions(+) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 248f09a4..3df6933d 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -718,6 +718,115 @@ public static JArray Select(this JArray source, Type resultType, string selector } #endregion Select + #region Single + /// + /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken Single(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJToken(queryable.Single(predicate, args))!; + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken Single(this JArray source, string predicate, params object?[] args) + { + return Single(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JToken Single(this JArray source, LambdaExpression lambda) + { + var queryable = ToQueryable(source); + return ToJToken(queryable.Single(lambda))!; + } + #endregion Single + + #region SingleOrDefault + /// + /// Returns the only element of a sequence, or a default value if the sequence + /// is empty; this method throws an exception if there is more than one element + /// in the sequence. + /// + /// A to return the single element of. + /// The single element of the input sequence, or default if the sequence contains no elements. + public static JToken? SingleOrDefault(this JArray source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.SingleOrDefault()); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence + /// is empty; and throws an exception if there is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken? SingleOrDefault(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJToken(queryable.SingleOrDefault(predicate, args)); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence + /// is empty; and throws an exception if there is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JToken? SingleOrDefault(this JArray source, string predicate, params object?[] args) + { + return SingleOrDefault(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence + /// is empty; and throws an exception if there is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JToken? SingleOrDefault(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJToken(queryable.SingleOrDefault(lambda)); + } + #endregion SingleOrDefault + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 1c572007..dfbd842f 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -848,6 +848,129 @@ public static JsonDocument Select(this JsonDocument source, Type resultType, str } #endregion Select + #region Single + /// + /// Returns the only element of a sequence, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// A to return the single element of. + /// The single element of the input sequence. + public static JsonElement Single(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Single()); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement Single(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJsonElement(queryable.Single(predicate, args)); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement Single(this JsonDocument source, string predicate, params object?[] args) + { + return Single(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there + /// is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JsonElement Single(this JsonDocument source, LambdaExpression lambda) + { + var queryable = ToQueryable(source); + return ToJsonElement(queryable.Single(lambda)); + } + #endregion Single + + #region SingleOrDefault + /// + /// Returns the only element of a sequence, or a default value if the sequence + /// is empty; this method throws an exception if there is more than one element + /// in the sequence. + /// + /// A to return the single element of. + /// The single element of the input sequence, or default if the sequence contains no elements. + public static JsonElement? SingleOrDefault(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.SingleOrDefault()); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence + /// is empty; and throws an exception if there is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement? SingleOrDefault(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return ToJsonElement(queryable.SingleOrDefault(predicate, args)); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence + /// is empty; and throws an exception if there is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The first element in source that passes the test in predicate. + public static JsonElement? SingleOrDefault(this JsonDocument source, string predicate, params object?[] args) + { + return SingleOrDefault(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence + /// is empty; and throws an exception if there is not exactly one element in the sequence. + /// + /// The to return the last element of. + /// A cached Lambda Expression. + /// The first element in source that passes the test in predicate. + public static JsonElement? SingleOrDefault(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonElement(queryable.SingleOrDefault(lambda)); + } + #endregion SingleOrDefault + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 842dd0ff..c1aee419 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -242,6 +242,23 @@ public void Select_ResultType() array.Should().ContainInOrder(1, 4, 9); } + [Fact] + public void Single() + { + // Act + Assert + ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); + } + + [Fact] + public void SingleOrDefault() + { + // Act + Assert 1 + ((string?)_source.LastOrDefault("Age > 30")!["Name"]).Should().Be("Doe"); + + // Act + Assert 2 + _source.LastOrDefault("Age > 999").Should().BeNull(); + } + [Fact] public void Where_Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 37888bc2..cd6d799e 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -240,6 +240,20 @@ public void Select() array.Should().BeEquivalentTo("John", "Doe"); } + [Fact] + public void Single() + { + // Act + Assert + _source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText()); + } + + [Fact] + public void SingleOrDefault() + { + // Act + Assert + _source.SingleOrDefault("Age > 999").Should().BeNull(); + } + [Fact] public void Select_ResultType() { From cf34f094f9668b4414713c3c9a708cadd61aad13 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 11:33:14 +0200 Subject: [PATCH 30/53] Skip --- .../NewtonsoftJsonExtensions.cs | 46 ++++++++++++------ .../SystemTextJsonExtensions.cs | 47 +++++++++++++++++++ .../NewtonsoftJsonTests.cs | 16 ++++++- .../SystemTextJsonTests.cs | 33 +++++++++++-- 4 files changed, 123 insertions(+), 19 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 3df6933d..eb2fafba 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -765,21 +765,6 @@ public static JToken Single(this JArray source, LambdaExpression lambda) #endregion Single #region SingleOrDefault - /// - /// Returns the only element of a sequence, or a default value if the sequence - /// is empty; this method throws an exception if there is more than one element - /// in the sequence. - /// - /// A to return the single element of. - /// The single element of the input sequence, or default if the sequence contains no elements. - public static JToken? SingleOrDefault(this JArray source) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJToken(queryable.SingleOrDefault()); - } - /// /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence /// is empty; and throws an exception if there is not exactly one element in the sequence. @@ -827,6 +812,37 @@ public static JToken Single(this JArray source, LambdaExpression lambda) } #endregion SingleOrDefault + #region SkipWhile + /// + /// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. + /// + /// A to return elements from. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. + public static JArray SkipWhile(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object[]? args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJArray(() => queryable.SkipWhile(predicate, args)); + } + + /// + /// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. + /// + /// A to return elements from. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. + public static JArray SkipWhile(this JArray source, string predicate, params object[]? args) + { + return SkipWhile(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + #endregion SkipWhile + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index dfbd842f..64755397 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -971,6 +971,53 @@ public static JsonElement Single(this JsonDocument source, LambdaExpression lamb } #endregion SingleOrDefault + #region Skip + /// + /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. + /// + /// A to return elements from. + /// The number of elements to skip before returning the remaining elements. + /// A that contains elements that occur after the specified index in the input sequence. + public static JsonDocument Skip(this JsonDocument source, int count) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.Skip(count)); + } + #endregion Skip + + #region SkipWhile + /// + /// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. + /// + /// A to return elements from. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. + public static JsonDocument SkipWhile(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object[]? args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.SkipWhile(predicate, args)); + } + + /// + /// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. + /// + /// A to return elements from. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. + public static JsonDocument SkipWhile(this JsonDocument source, string predicate, params object[]? args) + { + return SkipWhile(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + #endregion SkipWhile + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index c1aee419..d8fe839f 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -260,7 +260,21 @@ public void SingleOrDefault() } [Fact] - public void Where_Select() + public void SkipWhile() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JArray.Parse(json); + + // Act + var result = source.SkipWhile("it > 5"); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().ContainInOrder(6, 7, 8, 9, 0); + } + + [Fact] + public void Where_With_Select() { // Act var result = _source.Where("Age > 30").Select("Name"); diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index cd6d799e..cb4cba33 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -1,5 +1,4 @@ -using System.IO; -using System.Text.Json; +using System.Text.Json; using FluentAssertions; using Xunit; @@ -270,7 +269,35 @@ public void Select_ResultType() } [Fact] - public void Where_Select() + public void Skip() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.Skip(3); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetInt32()); + array.Should().ContainInOrder(4, 5, 6, 7, 8, 9, 0); + } + + [Fact] + public void SkipWhile() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.SkipWhile("it > 5"); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetInt32()); + array.Should().ContainInOrder(6, 7, 8, 9, 0); + } + + [Fact] + public void Where_With_Select() { // Act var result = _source.Where("Age > 30").Select("Name"); From 775788a08e28dc11ac94f563d8f2d5d92a083af8 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 11:42:00 +0200 Subject: [PATCH 31/53] min max --- .../NewtonsoftJsonExtensions.cs | 20 ++--- .../SystemTextJsonExtensions.cs | 87 ++++++++++++++++--- .../NewtonsoftJsonTests.cs | 4 +- .../SystemTextJsonTests.cs | 4 +- 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index eb2fafba..4604da77 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -474,13 +474,13 @@ public static JToken Last(this JArray source, LambdaExpression lambda) /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The max element in the sequence. - public static JToken Max(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + public static object Max(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJToken(queryable.Max(config, predicate, args))!; + return queryable.Max(config, predicate, args); } /// @@ -490,7 +490,7 @@ public static JToken Max(this JArray source, NewtonsoftJsonParsingConfig config, /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The max element in the sequence. - public static JToken Max(this JArray source, string predicate, params object?[] args) + public static object Max(this JArray source, string predicate, params object?[] args) { return Max(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } @@ -501,12 +501,12 @@ public static JToken Max(this JArray source, string predicate, params object?[] /// A sequence of values to calculate find the max for. /// A Lambda Expression. /// The max element in the sequence. - public static JToken Max(this JArray source, LambdaExpression lambda) + public static object Max(this JArray source, LambdaExpression lambda) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJToken(queryable.Max(lambda))!; + return queryable.Max(lambda); } #endregion Max @@ -519,13 +519,13 @@ public static JToken Max(this JArray source, LambdaExpression lambda) /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The min element in the sequence. - public static JToken Min(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + public static object Min(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJToken(queryable.Min(config, predicate, args))!; + return queryable.Min(config, predicate, args); } /// @@ -535,7 +535,7 @@ public static JToken Min(this JArray source, NewtonsoftJsonParsingConfig config, /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The min element in the sequence. - public static JToken Min(this JArray source, string predicate, params object?[] args) + public static object Min(this JArray source, string predicate, params object?[] args) { return Min(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } @@ -546,12 +546,12 @@ public static JToken Min(this JArray source, string predicate, params object?[] /// A sequence of values to calculate find the min for. /// A Lambda Expression. /// The min element in the sequence. - public static JToken Min(this JArray source, LambdaExpression lambda) + public static object Min(this JArray source, LambdaExpression lambda) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJToken(queryable.Min(lambda))!; + return queryable.Min(lambda); } #endregion Min diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 64755397..3d4c117a 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -565,12 +565,12 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda /// /// A sequence of values to calculate find the max for. /// The max element in the sequence. - public static JsonElement Max(this JsonDocument source) + public static object Max(this JsonDocument source) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Max()) ?? default; + return queryable.Max(); } /// @@ -581,13 +581,13 @@ public static JsonElement Max(this JsonDocument source) /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The max element in the sequence. - public static JsonElement Max(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + public static object Max(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.Max(config, predicate, args)) ?? default; + return queryable.Max(config, predicate, args); } /// @@ -597,7 +597,7 @@ public static JsonElement Max(this JsonDocument source, SystemTextJsonParsingCon /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The max element in the sequence. - public static JsonElement Max(this JsonDocument source, string predicate, params object?[] args) + public static object Max(this JsonDocument source, string predicate, params object?[] args) { return Max(source, SystemTextJsonParsingConfig.Default, predicate, args); } @@ -608,12 +608,12 @@ public static JsonElement Max(this JsonDocument source, string predicate, params /// A sequence of values to calculate find the max for. /// A Lambda Expression. /// The max element in the sequence. - public static JsonElement Max(this JsonDocument source, LambdaExpression lambda) + public static object Max(this JsonDocument source, LambdaExpression lambda) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Max(lambda)) ?? default; + return queryable.Max(lambda); } #endregion Max @@ -623,12 +623,12 @@ public static JsonElement Max(this JsonDocument source, LambdaExpression lambda) /// /// A sequence of values to calculate find the min for. /// The min element in the sequence. - public static JsonElement Min(this JsonDocument source) + public static object Min(this JsonDocument source) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Min()) ?? default; + return queryable.Min(); } /// @@ -639,13 +639,13 @@ public static JsonElement Min(this JsonDocument source) /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The min element in the sequence. - public static JsonElement Min(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + public static object Min(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.Min(config, predicate, args)) ?? default; + return queryable.Min(config, predicate, args); } /// @@ -655,7 +655,7 @@ public static JsonElement Min(this JsonDocument source, SystemTextJsonParsingCon /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// The min element in the sequence. - public static JsonElement Min(this JsonDocument source, string predicate, params object?[] args) + public static object Min(this JsonDocument source, string predicate, params object?[] args) { return Min(source, SystemTextJsonParsingConfig.Default, predicate, args); } @@ -666,12 +666,12 @@ public static JsonElement Min(this JsonDocument source, string predicate, params /// A sequence of values to calculate find the min for. /// A Lambda Expression. /// The min element in the sequence. - public static JsonElement Min(this JsonDocument source, LambdaExpression lambda) + public static object Min(this JsonDocument source, LambdaExpression lambda) { Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Min(lambda)) ?? default; + return queryable.Min(lambda); } #endregion Min @@ -1018,6 +1018,65 @@ public static JsonDocument SkipWhile(this JsonDocument source, string predicate, } #endregion SkipWhile + #region Sum + /// + /// Computes the sum of a sequence of numeric values. + /// + /// A sequence of numeric values to calculate the sum of. + /// The sum of the values in the sequence. + public static object Sum(this JsonDocument source) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return queryable.Sum(); + } + + /// + /// Computes the sum of a sequence of numeric values. + /// + /// A sequence of numeric values to calculate the sum of. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The sum of the values in the sequence. + public static object Sum(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config); + return queryable.Sum(predicate, args); + } + + /// + /// Computes the sum of a sequence of numeric values. + /// + /// A sequence of numeric values to calculate the sum of. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// The sum of the values in the sequence. + public static object Sum(this JsonDocument source, string predicate, params object?[] args) + { + return Sum(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + + /// + /// Computes the sum of a sequence of numeric values. + /// + /// A sequence of numeric values to calculate the sum of. + /// A Lambda Expression. + /// The sum of the values in the sequence. + public static object Sum(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + Check.NotNull(lambda); + + var queryable = ToQueryable(source); + return queryable.Sum(lambda); + } + #endregion Sum + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index d8fe839f..84cd5813 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -154,14 +154,14 @@ public void LastOrDefault() public void Max() { // Act + Assert - ((string?)_source.Max("Age")).Should().Be("40"); + ((int?)_source.Max("Age")).Should().Be(40); } [Fact] public void Min() { // Act + Assert - ((string?)_source.Min("Age")).Should().Be("30"); + ((int?)_source.Min("Age")).Should().Be(30); } [Fact] diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index cb4cba33..231f3237 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -166,14 +166,14 @@ public void LastOrDefault() public void Max() { // Act + Assert - _source.Max("Age").GetRawText().Should().BeEquivalentTo("40"); + _source.Max("Age").Should().BeEquivalentTo(40); } [Fact] public void Min() { // Act + Assert - _source.Min("Age").GetRawText().Should().BeEquivalentTo("30"); + _source.Min("Age").Should().BeEquivalentTo(30); } [Fact] From 8dc72ef8ba7270f5e0e12b7382193039d53d837f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 12:14:17 +0200 Subject: [PATCH 32/53] Take --- .../NewtonsoftJsonExtensions.cs | 31 ++++++++++++ .../SystemTextJsonExtensions.cs | 47 +++++++++++++++++++ .../DynamicQueryableExtensions.cs | 5 +- .../NewtonsoftJsonTests.cs | 28 +++++++++++ .../SystemTextJsonTests.cs | 28 +++++++++++ 5 files changed, 136 insertions(+), 3 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 4604da77..d7981402 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -843,6 +843,37 @@ public static JArray SkipWhile(this JArray source, string predicate, params obje } #endregion SkipWhile + #region TakeWhile + /// + /// Returns elements from a sequence as long as a specified condition is true. + /// + /// The sequence to return elements from. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from the input sequence occurring before the element at which the test specified by predicate no longer passes. + public static JArray TakeWhile(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJArray(() => queryable.TakeWhile(predicate, args)); + } + + /// + /// Returns elements from a sequence as long as a specified condition is true. + /// + /// The sequence to return elements from. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from the input sequence occurring before the element at which the test specified by predicate no longer passes. + public static JArray TakeWhile(this JArray source, string predicate, params object?[] args) + { + return TakeWhile(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + } + #endregion TakeWhile + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 3d4c117a..34bbf051 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -1077,6 +1077,53 @@ public static object Sum(this JsonDocument source, LambdaExpression lambda) } #endregion Sum + #region Take + /// + /// Returns a specified number of contiguous elements from the start of a sequence. + /// + /// The sequence to return elements from. + /// The number of elements to return. + /// A that contains the specified number of elements from the start of source. + public static JsonDocument Take(this JsonDocument source, int count) + { + Check.NotNull(source); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.Take(count)); + } + #endregion Take + + #region TakeWhile + /// + /// Returns elements from a sequence as long as a specified condition is true. + /// + /// The sequence to return elements from. + /// The . + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from the input sequence occurring before the element at which the test specified by predicate no longer passes. + public static JsonDocument TakeWhile(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.TakeWhile(predicate, args)); + } + + /// + /// Returns elements from a sequence as long as a specified condition is true. + /// + /// The sequence to return elements from. + /// A function to test each element for a condition. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An that contains elements from the input sequence occurring before the element at which the test specified by predicate no longer passes. + public static JsonDocument TakeWhile(this JsonDocument source, string predicate, params object?[] args) + { + return TakeWhile(source, SystemTextJsonParsingConfig.Default, predicate, args); + } + #endregion TakeWhile + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 116ff6cb..2c7ac3a1 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -2306,7 +2306,7 @@ public static IQueryable SkipWhile(this IQueryable source, ParsingConfig config, { Check.NotNull(source); Check.NotNull(config); - Check.NotNull(predicate, nameof(predicate)); + Check.NotNull(predicate); bool createParameterCtor = SupportsLinqToObjects(config, source); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); @@ -2436,7 +2436,7 @@ public static IQueryable TakeWhile(this IQueryable source, ParsingConfig config, { Check.NotNull(source); Check.NotNull(config); - Check.NotNull(predicate, nameof(predicate)); + Check.NotNull(predicate); bool createParameterCtor = SupportsLinqToObjects(config, source); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); @@ -2449,7 +2449,6 @@ public static IQueryable TakeWhile(this IQueryable source, string predicate, par { return TakeWhile(source, ParsingConfig.Default, predicate, args); } - #endregion TakeWhile #region ThenBy diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 84cd5813..cd3ac59f 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -273,6 +273,34 @@ public void SkipWhile() array.Should().ContainInOrder(6, 7, 8, 9, 0); } + [Fact] + public void Take() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JArray.Parse(json); + + // Act + var result = source.Take(3); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().ContainInOrder(1, 2, 3); + } + + [Fact] + public void TakeWhile() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JArray.Parse(json); + + // Act + var result = source.TakeWhile("it < 5"); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().ContainInOrder(1, 2, 3, 4); + } + [Fact] public void Where_With_Select() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 231f3237..87304e67 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -296,6 +296,34 @@ public void SkipWhile() array.Should().ContainInOrder(6, 7, 8, 9, 0); } + [Fact] + public void Take() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.Take(3); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetInt32()); + array.Should().ContainInOrder(1, 2, 3); + } + + [Fact] + public void TakeWhile() + { + var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.TakeWhile("it < 5"); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetInt32()); + array.Should().ContainInOrder(1, 2, 3, 4); + } + [Fact] public void Where_With_Select() { From 39e9e79dd185150258b764e95f5e4b4389a55f73 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 13:50:16 +0200 Subject: [PATCH 33/53] OrderBy_ThenBy --- .../NewtonsoftJsonExtensions.cs | 62 +++++++++++++++++++ .../SystemTextJsonExtensions.cs | 62 +++++++++++++++++++ .../DynamicQueryableExtensions.cs | 5 +- .../NewtonsoftJsonTests.cs | 27 ++++++++ .../SystemTextJsonTests.cs | 27 ++++++++ 5 files changed, 180 insertions(+), 3 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index d7981402..eceb98b0 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -874,6 +874,68 @@ public static JArray TakeWhile(this JArray source, string predicate, params obje } #endregion TakeWhile + #region ThenBy + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable + return ToJArray(() => queryable.ThenBy(ordering, args)); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable + return ToJArray(() => queryable.ThenBy(ordering, comparer, args)); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray ThenBy(this JArray source, string ordering, params object?[] args) + { + return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, args); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JArray ThenBy(this JArray source, string ordering, IComparer comparer, params object?[] args) + { + return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, comparer, args); + } + #endregion ThenBy + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 34bbf051..df855a15 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -1124,6 +1124,68 @@ public static JsonDocument TakeWhile(this JsonDocument source, string predicate, } #endregion TakeWhile + #region ThenBy + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable + return ToJsonDocumentArray(() => queryable.ThenBy(ordering, args)); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// The . + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) + { + Check.NotNull(source); + Check.NotNull(config); + + var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable; + return ToJsonDocumentArray(() => queryable.ThenBy(ordering, comparer, args)); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument ThenBy(this JsonDocument source, string ordering, params object?[] args) + { + return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, args); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + /// + /// A sequence of values to order. + /// An expression string to indicate values to order by. + /// The comparer to use. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// A whose elements are sorted according to the specified . + public static JsonDocument ThenBy(this JsonDocument source, string ordering, IComparer comparer, params object?[] args) + { + return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, comparer, args); + } + #endregion ThenBy + #region Where /// /// Filters a sequence of values based on a predicate. diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 2c7ac3a1..be5c1345 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -2550,7 +2550,7 @@ internal static IOrderedQueryable InternalThenBy(IOrderedQueryable source, Parsi { Check.NotNull(source); Check.NotNull(config); - Check.NotEmpty(ordering, nameof(ordering)); + Check.NotEmpty(ordering); ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty, config.RenameEmptyParameterExpressionNames) }; ExpressionParser parser = new ExpressionParser(parameters, ordering, args, config); @@ -2593,8 +2593,7 @@ public static IOrderedQueryable ThenBy(this IOrderedQueryable source, string ord { return ThenBy(source, ParsingConfig.Default, ordering, comparer, args); } - - #endregion OrderBy + #endregion ThenBy #region Where /// diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index cd3ac59f..7118631d 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -182,6 +182,33 @@ public void OrderBy() arrayAsc.Should().BeEquivalentTo("Doe", "John"); } + [Fact] + public void OrderBy_ThenBy() + { + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 40 + }, + { + ""Name"": ""Stef"", + ""Age"": 18 + } + ]"; + var source = JArray.Parse(json); + + // Act + var result = source.OrderBy("Age").ThenBy("Name").Select("Name"); + + // Assert + var array = result.Select(x => x.Value()); + array.Should().BeEquivalentTo("Doe", "John", "Stef"); + } + [Fact] public void Page() { diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 87304e67..6822ad17 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -194,6 +194,33 @@ public void OrderBy() arrayAsc.Should().BeEquivalentTo("Doe", "John"); } + [Fact] + public void OrderBy_ThenBy() + { + var json = @"[ + { + ""Name"": ""John"", + ""Age"": 30 + }, + { + ""Name"": ""Doe"", + ""Age"": 40 + }, + { + ""Name"": ""Stef"", + ""Age"": 18 + } + ]"; + var source = JsonDocument.Parse(json); + + // Act + var result = source.OrderBy("Age").ThenBy("Name").Select("Name"); + + // Assert + var array = result.RootElement.EnumerateArray().Select(x => x.GetString()); + array.Should().BeEquivalentTo("Doe", "John", "Stef"); + } + [Fact] public void Page() { From e8b4074c02f762fc4f7c485aa664fed98835eb56 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 13:52:12 +0200 Subject: [PATCH 34/53] Where --- .../NewtonsoftJsonExtensions.cs | 15 +++++++++++++++ .../SystemTextJsonExtensions.cs | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index eceb98b0..2cbfa12a 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -971,6 +971,21 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi var queryable = ToQueryable(source, config); return ToJArray(() => queryable.Where(config, predicate, args)); } + + /// + /// Filters a sequence of values based on a predicate. + /// + /// A to filter. + /// A cached Lambda Expression. + /// A that contains elements from the input sequence that satisfy the condition specified by LambdaExpression. + public static JArray Where(this JArray source, LambdaExpression lambda) + { + Check.NotNull(source); + Check.NotNull(lambda); + + var queryable = ToQueryable(source); + return ToJArray(() => queryable.Where(lambda)); + } #endregion Where #region Private Methods diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index df855a15..88b888a9 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -1211,11 +1211,25 @@ public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsing { Check.NotNull(source); Check.NotNull(config); - Check.NotNullOrEmpty(predicate); var queryable = ToQueryable(source, config); return ToJsonDocumentArray(() => queryable.Where(config, predicate, args)); } + + /// + /// Filters a sequence of values based on a predicate. + /// + /// A to filter. + /// A cached Lambda Expression. + /// A that contains elements from the input sequence that satisfy the condition specified by LambdaExpression. + public static JsonDocument Where(this JsonDocument source, LambdaExpression lambda) + { + Check.NotNull(source); + Check.NotNull(lambda); + + var queryable = ToQueryable(source); + return ToJsonDocumentArray(() => queryable.Where(lambda)); + } #endregion Where #region Private Methods From 82004ffa1ace18d7ea6fa9b3ad574c82c886ffd9 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 13:57:50 +0200 Subject: [PATCH 35/53] . --- .../DynamicQueryableExtensions.cs | 10 +- ...ueryableWithFormattableStringExtensions.cs | 844 +++++++++--------- 2 files changed, 426 insertions(+), 428 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index be5c1345..e72b3a5a 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -2025,7 +2025,7 @@ public static IQueryable SelectMany(this IQueryable source, st /// ]]> /// /// - public static IQueryable SelectMany(this IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, object[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs) + public static IQueryable SelectMany(this IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, object?[]? collectionSelectorArgs = null, params object?[]? resultSelectorArgs) { return SelectMany(source, collectionSelector, resultSelector, "x", "y", collectionSelectorArgs, resultSelectorArgs); } @@ -2107,7 +2107,7 @@ public static IQueryable SelectMany( } /// - public static IQueryable SelectMany(this IQueryable source, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs) + public static IQueryable SelectMany(this IQueryable source, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object?[]? resultSelectorArgs) { return SelectMany(source, ParsingConfig.Default, collectionSelector, resultSelector, collectionParameterName, resultParameterName, collectionSelectorArgs, resultSelectorArgs); } @@ -2302,7 +2302,7 @@ public static IQueryable Skip(this IQueryable source, int count) /// /// /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. - public static IQueryable SkipWhile(this IQueryable source, ParsingConfig config, string predicate, params object[]? args) + public static IQueryable SkipWhile(this IQueryable source, ParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -2315,7 +2315,7 @@ public static IQueryable SkipWhile(this IQueryable source, ParsingConfig config, } /// - public static IQueryable SkipWhile(this IQueryable source, string predicate, params object[]? args) + public static IQueryable SkipWhile(this IQueryable source, string predicate, params object?[] args) { return SkipWhile(source, ParsingConfig.Default, predicate, args); } @@ -2803,8 +2803,6 @@ private static TResult Execute(MethodInfo operatorMethodInfo, IQueryabl return (TResult)Convert.ChangeType(result, typeof(TResult)); } - - #endregion Private Helpers } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableWithFormattableStringExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableWithFormattableStringExtensions.cs index 8cc57e0b..e81ad188 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableWithFormattableStringExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableWithFormattableStringExtensions.cs @@ -4,429 +4,429 @@ using System.Text.RegularExpressions; using JetBrains.Annotations; -namespace System.Linq.Dynamic.Core -{ +namespace System.Linq.Dynamic.Core; + #if NET46_OR_GREATER || NET5_0_OR_GREATER || NETCOREAPP2_1_OR_GREATER || NETSTANDARD1_3_OR_GREATER || UAP10_0 - /// - /// Provides a set of static extension methods for querying data structures that implement . - /// It supports a FormattableString string as predicate. - /// - /// - public static class DynamicQueryableWithFormattableStringExtensions - { - private static readonly Regex ReplaceArgumentsRegex = new(@"{(\d+)}", RegexOptions.Compiled); - - public static IQueryable WhereInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Where(source, config, predicateStr, args); - } - - public static IQueryable WhereInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Where(source, predicateStr, args); - } - - public static IQueryable WhereInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Where(source, config, predicateStr, args); - } - - public static IQueryable WhereInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Where(source, predicateStr, args); - } - - [PublicAPI] - public static bool AllInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.All(source, predicateStr, args); - } - - [PublicAPI] - public static bool AllInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.All(source, config, predicateStr, args); - } - - [PublicAPI] - public static bool AnyInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Any(source, config, predicateStr, args); - } - - public static bool AnyInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Any(source, predicateStr, args); - } - - [PublicAPI] - public static double AverageInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Average(source, config, predicateStr, args); - } - - [PublicAPI] - public static double AverageInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Average(source, predicateStr, args); - } - - public static dynamic SingleInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Single(source, config, predicateStr, args); - } - - public static dynamic SingleInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Single(source, predicateStr, args); - } - - public static dynamic SingleOrDefaultInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.SingleOrDefault(source, config, predicateStr, args); - } - - public static dynamic SingleOrDefaultInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.SingleOrDefault(source, predicateStr, args); - } - - public static IQueryable SkipWhileInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.SkipWhile(source, config, predicateStr, args); - } - - public static IQueryable SkipWhileInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.SkipWhile(source, predicateStr, args); - } - - public static IQueryable TakeWhileInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.TakeWhile(source, config, predicateStr, args); - } - - public static IQueryable TakeWhileInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.TakeWhile(source, predicateStr, args); - } - - [PublicAPI] - public static object SumInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Sum(source, config, predicateStr, args); - } - - [PublicAPI] - public static object SumInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Sum(source, predicateStr, args); - } - - [PublicAPI] - public static int CountInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Count(source, config, predicateStr, args); - } - - public static int CountInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Count(source, predicateStr, args); - } - - public static dynamic FirstInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.First(source, config, predicateStr, args); - } - - public static dynamic FirstInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.First(source, predicateStr, args); - } - - public static dynamic FirstOrDefaultInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.FirstOrDefault(source, config, predicateStr, args); - } - - public static dynamic FirstOrDefaultInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.FirstOrDefault(source, predicateStr, args); - } - - public static dynamic LastInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Last(source, config, predicateStr, args); - } - - public static dynamic LastInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Last(source, predicateStr, args); - } - - public static dynamic LastOrDefaultInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.LastOrDefault(source, config, predicateStr, args); - } - - public static dynamic LastOrDefaultInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.LastOrDefault(source, predicateStr, args); - } - - [PublicAPI] - public static long LongCountInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.LongCount(source, config, predicateStr, args); - } - - public static long LongCountInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.LongCount(source, predicateStr, args); - } - - [PublicAPI] - public static object MaxInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Max(source, config, predicateStr, args); - } - - [PublicAPI] - public static object MaxInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Max(source, predicateStr, args); - } - - [PublicAPI] - public static object MinInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Min(source, config, predicateStr, args); - } - - [PublicAPI] - public static object MinInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString predicate) - { - var predicateStr = ParseFormattableString(predicate, out var args); - return DynamicQueryableExtensions.Min(source, predicateStr, args); - } - - public static IQueryable SelectInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.Select(source, config, selectorStr, args); - } - - public static IQueryable SelectInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] Type resultType, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.Select(source, config, resultType, selectorStr, args); - } - - public static IQueryable SelectInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.Select(source, selectorStr, args); - } - - public static IQueryable SelectInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.Select(source, config, selectorStr, args); - } - - public static IQueryable SelectInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.Select(source, selectorStr, args); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString collectionSelector, [NotNull] FormattableString resultSelector) - { - var collectionSelectorStr = ParseFormattableString(collectionSelector, out var collectionSelectorArgs); - var resultSelectorStr = ParseFormattableString(resultSelector, out var resultSelectorArgs); - return DynamicQueryableExtensions.SelectMany(source, config, collectionSelectorStr, resultSelectorStr, collectionSelectorArgs, resultSelectorArgs); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.SelectMany(source, config, selectorStr, args); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] Type resultType, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.SelectMany(source, config, resultType, selectorStr, args); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString collectionSelector, [NotNull] FormattableString resultSelector) - { - var collectionSelectorStr = ParseFormattableString(collectionSelector, out var collectionSelectorArgs); - var resultSelectorStr = ParseFormattableString(resultSelector, out var resultSelectorArgs); - return DynamicQueryableExtensions.SelectMany(source, collectionSelectorStr, collectionSelectorArgs, resultSelectorStr, resultSelectorArgs); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.SelectMany(source, selectorStr, args); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] Type resultType, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.SelectMany(source, resultType, selectorStr, args); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.SelectMany(source, config, selectorStr, args); - } - - public static IQueryable SelectManyInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString selector) - { - var selectorStr = ParseFormattableString(selector, out var args); - return DynamicQueryableExtensions.SelectMany(source, selectorStr, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, config, orderingStr, comparer, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, config, orderingStr, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, orderingStr, comparer, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, orderingStr, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, config, orderingStr, comparer, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, config, orderingStr, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, orderingStr, comparer, args); - } - - public static IOrderedQueryable OrderByInterpolated([NotNull] this IQueryable source, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.OrderBy(source, orderingStr, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, config, orderingStr, comparer, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, config, orderingStr, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, orderingStr, comparer, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, orderingStr, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, config, orderingStr, comparer, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] ParsingConfig config, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, config, orderingStr, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] FormattableString ordering, IComparer comparer) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, orderingStr, comparer, args); - } - - public static IOrderedQueryable ThenByInterpolated([NotNull] this IOrderedQueryable source, [NotNull] FormattableString ordering) - { - var orderingStr = ParseFormattableString(ordering, out var args); - return DynamicQueryableExtensions.ThenBy(source, orderingStr, args); - } - - private static string ParseFormattableString(FormattableString predicate, out object[] args) - { - args = predicate.GetArguments(); - return ReplaceArgumentsRegex.Replace(predicate.Format, "@$1"); // replace {0} with @0 - } +/// +/// Provides a set of static extension methods for querying data structures that implement . +/// It supports a FormattableString string as predicate. +/// +/// +public static class DynamicQueryableWithFormattableStringExtensions +{ + private static readonly Regex ReplaceArgumentsRegex = new(@"{(\d+)}", RegexOptions.Compiled); + + public static IQueryable WhereInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Where(config, predicateStr, args); + } + + public static IQueryable WhereInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Where(predicateStr, args); + } + + public static IQueryable WhereInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Where(config, predicateStr, args); + } + + public static IQueryable WhereInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Where(predicateStr, args); + } + + [PublicAPI] + public static bool AllInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.All(predicateStr, args); + } + + [PublicAPI] + public static bool AllInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.All(config, predicateStr, args); + } + + [PublicAPI] + public static bool AnyInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Any(config, predicateStr, args); + } + + public static bool AnyInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Any(predicateStr, args); + } + + [PublicAPI] + public static double AverageInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Average(config, predicateStr, args); + } + + [PublicAPI] + public static double AverageInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Average(predicateStr, args); + } + + public static dynamic SingleInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Single(config, predicateStr, args); + } + + public static dynamic SingleInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Single(predicateStr, args); + } + + public static dynamic SingleOrDefaultInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.SingleOrDefault(config, predicateStr, args); + } + + public static dynamic SingleOrDefaultInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.SingleOrDefault(predicateStr, args); + } + + public static IQueryable SkipWhileInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.SkipWhile(config, predicateStr, args); + } + + public static IQueryable SkipWhileInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.SkipWhile(predicateStr, args); + } + + public static IQueryable TakeWhileInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.TakeWhile(config, predicateStr, args); + } + + public static IQueryable TakeWhileInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.TakeWhile(predicateStr, args); + } + + [PublicAPI] + public static object SumInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Sum(config, predicateStr, args); + } + + [PublicAPI] + public static object SumInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Sum(predicateStr, args); + } + + [PublicAPI] + public static int CountInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Count(config, predicateStr, args); + } + + public static int CountInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Count(predicateStr, args); + } + + public static dynamic FirstInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.First(config, predicateStr, args); + } + + public static dynamic FirstInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.First(predicateStr, args); + } + + public static dynamic FirstOrDefaultInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.FirstOrDefault(config, predicateStr, args); + } + + public static dynamic FirstOrDefaultInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.FirstOrDefault(predicateStr, args); + } + + public static dynamic LastInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Last(config, predicateStr, args); + } + + public static dynamic LastInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Last(predicateStr, args); + } + + public static dynamic LastOrDefaultInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.LastOrDefault(config, predicateStr, args); + } + + public static dynamic LastOrDefaultInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.LastOrDefault(predicateStr, args); + } + + [PublicAPI] + public static long LongCountInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.LongCount(config, predicateStr, args); + } + + public static long LongCountInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.LongCount(predicateStr, args); + } + + [PublicAPI] + public static object MaxInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Max(config, predicateStr, args); + } + + [PublicAPI] + public static object MaxInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Max(predicateStr, args); + } + + [PublicAPI] + public static object MinInterpolated(this IQueryable source, ParsingConfig config, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Min(config, predicateStr, args); + } + + [PublicAPI] + public static object MinInterpolated(this IQueryable source, FormattableString predicate) + { + var predicateStr = ParseFormattableString(predicate, out var args); + return source.Min(predicateStr, args); + } + + public static IQueryable SelectInterpolated(this IQueryable source, ParsingConfig config, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.Select(config, selectorStr, args); + } + + public static IQueryable SelectInterpolated(this IQueryable source, ParsingConfig config, Type resultType, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.Select(config, resultType, selectorStr, args); + } + + public static IQueryable SelectInterpolated(this IQueryable source, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.Select(selectorStr, args); + } + + public static IQueryable SelectInterpolated(this IQueryable source, ParsingConfig config, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.Select(config, selectorStr, args); + } + + public static IQueryable SelectInterpolated(this IQueryable source, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.Select(selectorStr, args); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, ParsingConfig config, FormattableString collectionSelector, FormattableString resultSelector) + { + var collectionSelectorStr = ParseFormattableString(collectionSelector, out var collectionSelectorArgs); + var resultSelectorStr = ParseFormattableString(resultSelector, out var resultSelectorArgs); + return source.SelectMany(config, collectionSelectorStr, resultSelectorStr, collectionSelectorArgs, resultSelectorArgs); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, ParsingConfig config, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.SelectMany(config, selectorStr, args); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, ParsingConfig config, Type resultType, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.SelectMany(config, resultType, selectorStr, args); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, FormattableString collectionSelector, FormattableString resultSelector) + { + var collectionSelectorStr = ParseFormattableString(collectionSelector, out var collectionSelectorArgs); + var resultSelectorStr = ParseFormattableString(resultSelector, out var resultSelectorArgs); + return source.SelectMany(collectionSelectorStr, collectionSelectorArgs, resultSelectorStr, resultSelectorArgs); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.SelectMany(selectorStr, args); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, Type resultType, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.SelectMany(resultType, selectorStr, args); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, ParsingConfig config, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.SelectMany(config, selectorStr, args); + } + + public static IQueryable SelectManyInterpolated(this IQueryable source, FormattableString selector) + { + var selectorStr = ParseFormattableString(selector, out var args); + return source.SelectMany(selectorStr, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, ParsingConfig config, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(config, orderingStr, comparer, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, ParsingConfig config, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(config, orderingStr, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(orderingStr, comparer, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(orderingStr, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, ParsingConfig config, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(config, orderingStr, comparer, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, ParsingConfig config, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(config, orderingStr, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(orderingStr, comparer, args); + } + + public static IOrderedQueryable OrderByInterpolated(this IQueryable source, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.OrderBy(orderingStr, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, ParsingConfig config, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(config, orderingStr, comparer, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, ParsingConfig config, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(config, orderingStr, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(orderingStr, comparer, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(orderingStr, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, ParsingConfig config, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(config, orderingStr, comparer, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, ParsingConfig config, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(config, orderingStr, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, FormattableString ordering, IComparer comparer) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(orderingStr, comparer, args); + } + + public static IOrderedQueryable ThenByInterpolated(this IOrderedQueryable source, FormattableString ordering) + { + var orderingStr = ParseFormattableString(ordering, out var args); + return source.ThenBy(orderingStr, args); + } + + private static string ParseFormattableString(FormattableString predicate, out object?[] args) + { + args = predicate.GetArguments(); + return ReplaceArgumentsRegex.Replace(predicate.Format, "@$1"); // replace {0} with @0 } -#endif } +#endif + #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member \ No newline at end of file From b0b7fd55122ec51970b17adf6025fa0433bdf823 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Apr 2024 15:03:03 +0200 Subject: [PATCH 36/53] DynamicClassFactory.CreateInstance --- .../Extensions/JObjectExtensions.cs | 20 +------ .../Models/DynamicPropertyWithValue.cs | 11 ---- .../NewtonsoftJsonExtensions.cs | 6 +- .../Extensions/JsonDocumentExtensions.cs | 15 +---- .../Models/DynamicPropertyWithValue.cs | 11 ---- .../SystemTextJsonExtensions.cs | 4 +- .../DynamicClassFactory.cs | 26 ++++++++- .../DynamicProperty.cs | 55 +++++++++---------- .../DynamicPropertyWithValue.cs | 22 ++++++++ .../DynamicQueryableExtensions.cs | 2 +- 10 files changed, 81 insertions(+), 91 deletions(-) delete mode 100644 src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs delete mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs create mode 100644 src/System.Linq.Dynamic.Core/DynamicPropertyWithValue.cs diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs index 0bc92fc9..11cc21e7 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.Linq.Dynamic.Core.NewtonsoftJson.Models; using System.Reflection; using JsonConverter.Abstractions.Models; using Newtonsoft.Json.Linq; @@ -35,11 +34,6 @@ private class JTokenResolvers : Dictionary o.Value() }, }; - //internal static object? ToDynamicClass(this JValue src) - //{ - // return src.Value; - //} - internal static DynamicClass? ToDynamicClass(this JObject? src, DynamicJsonClassOptions? options = null) { if (src == null) @@ -58,7 +52,7 @@ private class JTokenResolvers : Dictionary(IEnumerable src) { return src.Cast().ToArray(); } - - private static DynamicClass CreateInstance(IList dynamicPropertiesWithValue) - { - var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray()); - var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; - foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) - { - dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); - } - - return dynamicClass; - } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs deleted file mode 100644 index 3734ec58..00000000 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Models/DynamicPropertyWithValue.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace System.Linq.Dynamic.Core.NewtonsoftJson.Models; - -internal class DynamicPropertyWithValue : DynamicProperty -{ - public object? Value { get; } - - public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object)) - { - Value = value; - } -} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 2cbfa12a..232f0526 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -821,7 +821,7 @@ public static JToken Single(this JArray source, LambdaExpression lambda) /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. - public static JArray SkipWhile(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object[]? args) + public static JArray SkipWhile(this JArray source, NewtonsoftJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -837,7 +837,7 @@ public static JArray SkipWhile(this JArray source, NewtonsoftJsonParsingConfig c /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. - public static JArray SkipWhile(this JArray source, string predicate, params object[]? args) + public static JArray SkipWhile(this JArray source, string predicate, params object?[] args) { return SkipWhile(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } @@ -887,7 +887,7 @@ public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig conf { Check.NotNull(source); Check.NotNull(config); - + var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable return ToJArray(() => queryable.ThenBy(ordering, args)); } diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs index d7b6d8a2..fde28619 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.Linq.Dynamic.Core.SystemTextJson.Models; using System.Reflection; using System.Text.Json; using JsonConverter.Abstractions.Models; @@ -43,7 +42,7 @@ private class JTokenResolvers : Dictionary(IEnumerable src) { return src.Cast().ToArray(); } - - private static DynamicClass CreateInstance(IList dynamicPropertiesWithValue) - { - var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray()); - var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; - foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) - { - dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); - } - - return dynamicClass; - } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs deleted file mode 100644 index 806703f3..00000000 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Models/DynamicPropertyWithValue.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace System.Linq.Dynamic.Core.SystemTextJson.Models; - -internal class DynamicPropertyWithValue : DynamicProperty -{ - public object? Value { get; } - - public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object)) - { - Value = value; - } -} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 88b888a9..ae8b34c8 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -996,7 +996,7 @@ public static JsonDocument Skip(this JsonDocument source, int count) /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. - public static JsonDocument SkipWhile(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object[]? args) + public static JsonDocument SkipWhile(this JsonDocument source, SystemTextJsonParsingConfig config, string predicate, params object?[] args) { Check.NotNull(source); Check.NotNull(config); @@ -1012,7 +1012,7 @@ public static JsonDocument SkipWhile(this JsonDocument source, SystemTextJsonPar /// A function to test each element for a condition. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. - public static JsonDocument SkipWhile(this JsonDocument source, string predicate, params object[]? args) + public static JsonDocument SkipWhile(this JsonDocument source, string predicate, params object?[] args) { return SkipWhile(source, SystemTextJsonParsingConfig.Default, predicate, args); } diff --git a/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs b/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs index 4b449743..914c45dc 100644 --- a/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs +++ b/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs @@ -10,7 +10,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading; -using JetBrains.Annotations; + #if WINDOWS_APP using System.Linq; #endif @@ -149,6 +149,28 @@ public static Type CreateGenericComparerType(Type comparerGenericType, Type comp return type; } + /// + /// Create a new instance of a dynamic class with a given set of public properties (with the value). + /// In case the value is null, the property is not set. + /// + /// A list of DynamicProperties with a value + /// Create a constructor with parameters. Default set to true. Note that for Linq-to-Database objects, this needs to be set to false. + /// Instance of + public static DynamicClass CreateInstance(IList dynamicPropertiesWithValue, bool createParameterCtor = true) + { + Check.HasNoNulls(dynamicPropertiesWithValue); + + var type = CreateType(dynamicPropertiesWithValue.Cast().ToArray(), createParameterCtor); + var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; + + foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) + { + dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); + } + + return dynamicClass; + } + /// /// The CreateType method creates a new data class with a given set of public properties and returns the System.Type object for the newly created class. If a data class with an identical sequence of properties has already been created, the System.Type object for this class is returned. /// Data classes implement private instance variables and read/write property accessors for the specified properties.Data classes also override the Equals and GetHashCode members to implement by-value equality. @@ -172,7 +194,7 @@ public static Type CreateGenericComparerType(Type comparerGenericType, Type comp /// public static Type CreateType(IList properties, bool createParameterCtor = true) { - Check.HasNoNulls(properties, nameof(properties)); + Check.HasNoNulls(properties); Type[] types = properties.Select(p => p.Type).ToArray(); string[] names = properties.Select(p => p.Name).ToArray(); diff --git a/src/System.Linq.Dynamic.Core/DynamicProperty.cs b/src/System.Linq.Dynamic.Core/DynamicProperty.cs index 10939f49..15922b86 100644 --- a/src/System.Linq.Dynamic.Core/DynamicProperty.cs +++ b/src/System.Linq.Dynamic.Core/DynamicProperty.cs @@ -1,35 +1,34 @@ -namespace System.Linq.Dynamic.Core +namespace System.Linq.Dynamic.Core; + +/// +/// DynamicProperty +/// +public class DynamicProperty { /// - /// DynamicProperty + /// Initializes a new instance of the class. /// - public class DynamicProperty + /// The name from the property. + /// The type from the property. + public DynamicProperty(string name, Type type) { - /// - /// Initializes a new instance of the class. - /// - /// The name from the property. - /// The type from the property. - public DynamicProperty(string name, Type type) - { - Name = name; - Type = type; - } + Name = name; + Type = type; + } - /// - /// Gets the name from the property. - /// - /// - /// The name from the property. - /// - public string Name { get; } + /// + /// Gets the name from the property. + /// + /// + /// The name from the property. + /// + public string Name { get; } - /// - /// Gets the type from the property. - /// - /// - /// The type from the property. - /// - public Type Type { get; } - } + /// + /// Gets the type from the property. + /// + /// + /// The type from the property. + /// + public Type Type { get; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/DynamicPropertyWithValue.cs b/src/System.Linq.Dynamic.Core/DynamicPropertyWithValue.cs new file mode 100644 index 00000000..8b61e76a --- /dev/null +++ b/src/System.Linq.Dynamic.Core/DynamicPropertyWithValue.cs @@ -0,0 +1,22 @@ +namespace System.Linq.Dynamic.Core; + +/// +/// DynamicPropertyWithValue +/// +public class DynamicPropertyWithValue : DynamicProperty +{ + /// + /// Gets the value from the property. + /// + public object? Value { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The name from the property. + /// The value from the property. + public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object)) + { + Value = value; + } +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index e72b3a5a..cbe76ce0 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -2314,7 +2314,7 @@ public static IQueryable SkipWhile(this IQueryable source, ParsingConfig config, return CreateQuery(_skipWhilePredicate, source, lambda); } - /// + /// public static IQueryable SkipWhile(this IQueryable source, string predicate, params object?[] args) { return SkipWhile(source, ParsingConfig.Default, predicate, args); From 44cc5a882dee00efa87eb0c257534563b775f7a0 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 11 Apr 2024 21:18:36 +0200 Subject: [PATCH 37/53] Update JsonDocumentExtensions.cs --- .../Extensions/JsonDocumentExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs index fde28619..075ba135 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -107,7 +107,7 @@ private static object ConvertNumber(JsonElement arg, DynamicJsonClassOptions? op return @byte; } - throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to int, long, double or decimal."); + throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to int, long, double, decimal or byte."); } private static IEnumerable ConvertJsonElementToEnumerable(JsonElement arg, DynamicJsonClassOptions? options = null) @@ -140,4 +140,4 @@ private static T[] ConvertToTypedArrayGeneric(IEnumerable src) { return src.Cast().ToArray(); } -} \ No newline at end of file +} From 6fc0f814e8fa835cc021c33a68a696dbf390a2c5 Mon Sep 17 00:00:00 2001 From: paule96 Date: Mon, 15 Apr 2024 10:46:03 +0200 Subject: [PATCH 38/53] fix compile errors (#797) Co-authored-by: Paul Jeschke --- .../SystemTextJsonExtensions.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index ae8b34c8..9434ad65 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -14,6 +14,7 @@ namespace System.Linq.Dynamic.Core.SystemTextJson; /// public static class SystemTextJsonExtensions { + private const string sequenceEmpty = "Sequence contains no elements"; #region Aggregate /// /// Dynamically runs an aggregate function on the >. @@ -338,7 +339,7 @@ public static JsonElement First(this JsonDocument source) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.First()); + return ToJsonElement(queryable.First()) ?? throw new InvalidOperationException(sequenceEmpty); } /// @@ -355,7 +356,7 @@ public static JsonElement First(this JsonDocument source, SystemTextJsonParsingC Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.First(config, predicate, args)); + return ToJsonElement(queryable.First(config, predicate, args)) ?? throw new InvalidOperationException(sequenceEmpty); } /// @@ -381,7 +382,7 @@ public static JsonElement First(this JsonDocument source, LambdaExpression lambd Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.First(lambda)); + return ToJsonElement(queryable.First(lambda)) ?? throw new InvalidOperationException(sequenceEmpty); } #endregion First @@ -454,7 +455,7 @@ public static JsonElement Last(this JsonDocument source) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Last()); + return ToJsonElement(queryable.Last()) ?? throw new InvalidOperationException(sequenceEmpty); } /// @@ -471,7 +472,7 @@ public static JsonElement Last(this JsonDocument source, SystemTextJsonParsingCo Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.Last(predicate, args)); + return ToJsonElement(queryable.Last(predicate, args)) ?? throw new InvalidOperationException(sequenceEmpty); } /// @@ -497,7 +498,7 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Last(lambda)); + return ToJsonElement(queryable.Last(lambda)) ?? throw new InvalidOperationException(sequenceEmpty); } #endregion Last @@ -860,7 +861,7 @@ public static JsonElement Single(this JsonDocument source) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Single()); + return ToJsonElement(queryable.Single()) ?? throw new InvalidOperationException(sequenceEmpty); } /// @@ -878,7 +879,7 @@ public static JsonElement Single(this JsonDocument source, SystemTextJsonParsing Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.Single(predicate, args)); + return ToJsonElement(queryable.Single(predicate, args)) ?? throw new InvalidOperationException(sequenceEmpty); } /// @@ -904,7 +905,7 @@ public static JsonElement Single(this JsonDocument source, string predicate, par public static JsonElement Single(this JsonDocument source, LambdaExpression lambda) { var queryable = ToQueryable(source); - return ToJsonElement(queryable.Single(lambda)); + return ToJsonElement(queryable.Single(lambda)) ?? throw new InvalidOperationException(sequenceEmpty); } #endregion Single From 2b7ef0334286cd36bc2d3eb05fc39d9afcd60db6 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 15 Apr 2024 10:57:07 +0200 Subject: [PATCH 39/53] some updates --- .../NewtonsoftJsonExtensions.cs | 15 ++++++++------- ...em.Linq.Dynamic.Core.NewtonsoftJson.csproj | 2 +- ...em.Linq.Dynamic.Core.SystemTextJson.csproj | 3 +-- .../SystemTextJsonExtensions.cs | 19 +++++++++---------- src/System.Linq.Dynamic.Core/Res.cs | 1 + 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 232f0526..5b34c6c7 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -300,7 +300,7 @@ public static JToken First(this JArray source, NewtonsoftJsonParsingConfig confi Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJToken(queryable.First(config, predicate, args)); + return ToJToken(queryable.First(config, predicate, args)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -312,7 +312,7 @@ public static JToken First(this JArray source, NewtonsoftJsonParsingConfig confi /// The first element in source that passes the test in predicate. public static JToken First(this JArray source, string predicate, params object?[] args) { - return First(source, NewtonsoftJsonParsingConfig.Default, predicate, args); + return First(source, NewtonsoftJsonParsingConfig.Default, predicate, args) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -326,7 +326,7 @@ public static JToken First(this JArray source, LambdaExpression lambda) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJToken(queryable.First(lambda)); + return ToJToken(queryable.First(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } #endregion First @@ -390,7 +390,7 @@ public static JToken Last(this JArray source, NewtonsoftJsonParsingConfig config Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJToken(queryable.Last(predicate, args)); + return ToJToken(queryable.Last(predicate, args)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -416,7 +416,7 @@ public static JToken Last(this JArray source, LambdaExpression lambda) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJToken(queryable.Last(lambda)); + return ToJToken(queryable.Last(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } #endregion Last @@ -734,7 +734,7 @@ public static JToken Single(this JArray source, NewtonsoftJsonParsingConfig conf Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJToken(queryable.Single(predicate, args))!; + return ToJToken(queryable.Single(predicate, args)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -760,7 +760,7 @@ public static JToken Single(this JArray source, string predicate, params object? public static JToken Single(this JArray source, LambdaExpression lambda) { var queryable = ToQueryable(source); - return ToJToken(queryable.Single(lambda))!; + return ToJToken(queryable.Single(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } #endregion Single @@ -1003,6 +1003,7 @@ public static JArray Where(this JArray source, LambdaExpression lambda) return JToken.FromObject(value); } + private static JArray ToJArray(Func func) { var array = new JArray(); diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj index ddf3dbde..9274c4c6 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj @@ -5,7 +5,7 @@ ../System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk System.Linq.Dynamic.Core Stef Heyenrath - TODO + Contains some extensions for System.Linq.Dynamic.Core to dynamically query a Newtonsoft.Json.JArray system;linq;dynamic;core;dotnet;json {8C5851B8-5C47-4229-AB55-D4252703598E} net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj index 41fac28e..0d928eb5 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -5,12 +5,11 @@ ../System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk System.Linq.Dynamic.Core Stef Heyenrath - TODO + Contains some extensions for System.Linq.Dynamic.Core to dynamically query a System.Text.Json.JsonDocument system;linq;dynamic;core;dotnet;json {FA01CE15-315A-499E-AFC2-955CA7EB45FF} netstandard2.0;netstandard2.1;net6.0;net8.0 1.3.$(PatchVersion) - fa8622ae-b712-4c03-8963-0d24e0dba084 diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 9434ad65..210f7fbd 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -14,7 +14,6 @@ namespace System.Linq.Dynamic.Core.SystemTextJson; /// public static class SystemTextJsonExtensions { - private const string sequenceEmpty = "Sequence contains no elements"; #region Aggregate /// /// Dynamically runs an aggregate function on the >. @@ -339,7 +338,7 @@ public static JsonElement First(this JsonDocument source) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.First()) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.First()) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -356,7 +355,7 @@ public static JsonElement First(this JsonDocument source, SystemTextJsonParsingC Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.First(config, predicate, args)) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.First(config, predicate, args)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -382,7 +381,7 @@ public static JsonElement First(this JsonDocument source, LambdaExpression lambd Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.First(lambda)) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.First(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } #endregion First @@ -455,7 +454,7 @@ public static JsonElement Last(this JsonDocument source) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Last()) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.Last()) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -472,7 +471,7 @@ public static JsonElement Last(this JsonDocument source, SystemTextJsonParsingCo Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.Last(predicate, args)) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.Last(predicate, args)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -498,7 +497,7 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Last(lambda)) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.Last(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } #endregion Last @@ -861,7 +860,7 @@ public static JsonElement Single(this JsonDocument source) Check.NotNull(source); var queryable = ToQueryable(source); - return ToJsonElement(queryable.Single()) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.Single()) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -879,7 +878,7 @@ public static JsonElement Single(this JsonDocument source, SystemTextJsonParsing Check.NotNull(config); var queryable = ToQueryable(source, config); - return ToJsonElement(queryable.Single(predicate, args)) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.Single(predicate, args)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } /// @@ -905,7 +904,7 @@ public static JsonElement Single(this JsonDocument source, string predicate, par public static JsonElement Single(this JsonDocument source, LambdaExpression lambda) { var queryable = ToQueryable(source); - return ToJsonElement(queryable.Single(lambda)) ?? throw new InvalidOperationException(sequenceEmpty); + return ToJsonElement(queryable.Single(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } #endregion Single diff --git a/src/System.Linq.Dynamic.Core/Res.cs b/src/System.Linq.Dynamic.Core/Res.cs index 4b074e08..fc5e2ee9 100644 --- a/src/System.Linq.Dynamic.Core/Res.cs +++ b/src/System.Linq.Dynamic.Core/Res.cs @@ -69,6 +69,7 @@ internal static class Res public const string OpenParenOrIdentifierExpected = "'(' or Identifier expected"; public const string OutKeywordRequiresDiscard = "When using an out variable, a discard '_' is required."; public const string ParseExceptionFormat = "{0} (at index {1})"; + public const string SequenceContainsNoElements = "Sequence contains no elements"; public const string SyntaxError = "Syntax error"; public const string TokenExpected = "{0} expected"; public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; From 6cb4317ea929b6ddc2a5ebcb7a316deba2dc08ee Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 23 Apr 2024 21:03:57 +0200 Subject: [PATCH 40/53] 1.4 --- .../System.Linq.Dynamic.Core.NewtonsoftJson.csproj | 2 +- .../System.Linq.Dynamic.Core.SystemTextJson.csproj | 2 +- src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj | 2 +- version.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj index 9274c4c6..7fd1effd 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj @@ -9,7 +9,7 @@ system;linq;dynamic;core;dotnet;json {8C5851B8-5C47-4229-AB55-D4252703598E} net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net8.0 - 1.3.$(PatchVersion) + 1.4.$(PatchVersion) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj index 0d928eb5..55d5ece5 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -9,7 +9,7 @@ system;linq;dynamic;core;dotnet;json {FA01CE15-315A-499E-AFC2-955CA7EB45FF} netstandard2.0;netstandard2.1;net6.0;net8.0 - 1.3.$(PatchVersion) + 1.4.$(PatchVersion) diff --git a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj index b7797926..ab7bced3 100644 --- a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj +++ b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj @@ -10,7 +10,7 @@ system;linq;dynamic;core;dotnet;NETCoreApp;NETStandard {D3804228-91F4-4502-9595-39584E510002} net35;net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;uap10.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 - 1.3.$(PatchVersion) + 1.4.$(PatchVersion) diff --git a/version.xml b/version.xml index 5d7319d1..882f9fd0 100644 --- a/version.xml +++ b/version.xml @@ -1,5 +1,5 @@ - 12 + 0-preview-01 \ No newline at end of file From fdd3c36bf93d9225226edcc9665a1f1f815b03c5 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 7 May 2024 21:28:46 +0200 Subject: [PATCH 41/53] """ --- .../NewtonsoftJsonTests.cs | 79 +++++++++++-------- .../DynamicExpressionParserTests.cs | 42 ++++++++++ .../SystemTextJsonTests.cs | 79 +++++++++++-------- 3 files changed, 130 insertions(+), 70 deletions(-) create mode 100644 test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 7118631d..38db907d 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -6,16 +6,19 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests; public class NewtonsoftJsonTests { - private const string ExampleJson = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - } - ]"; + private const string ExampleJson = + """ + [ + { + "Name": "John", + "Age": 30 + }, + { + "Name": "Doe", + "Age": 40 + } + ] + """; private readonly JArray _source = JArray.Parse(ExampleJson); [Fact] @@ -96,17 +99,20 @@ public void Count() [Fact] public void Distinct() { - var json = @"[ - { - ""Name"": ""John"" - }, - { - ""Name"": ""Doe"" - }, - { - ""Name"": ""John"" - } - ]"; + var json = + """ + [ + { + "Name": "John" + }, + { + "Name": "Doe" + }, + { + "Name": "John" + } + ] + """; var source = JArray.Parse(json); // Act @@ -185,20 +191,23 @@ public void OrderBy() [Fact] public void OrderBy_ThenBy() { - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - }, - { - ""Name"": ""Stef"", - ""Age"": 18 - } - ]"; + var json = + """ + [ + { + "Name": "John", + "Age": 30 + }, + { + "Name": "Doe", + "Age": 40 + }, + { + "Name": "Stef", + "Age": 18 + } + ] + """; var source = JArray.Parse(json); // Act diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs new file mode 100644 index 00000000..6e174735 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Text.Json; +using FluentAssertions; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; + +[ExcludeFromCodeCoverage] +public class DynamicExpressionParserTests +{ + private const string ExampleJson = + """ + [ + { + "Name": "John", + "Age": 30 + }, + { + "Name": "Doe", + "Age": 40 + } + ] + """; + private readonly JsonDocument _source = JsonDocument.Parse(ExampleJson); + + //[Fact] + // This is not supported yet... + public void X() + { + // Act + var qry = _source.RootElement.EnumerateArray(); + var parameters = new[] { Expression.Parameter(_source.GetType(), "y") }; + + // Assert + var lambdaExpression = DynamicExpressionParser.ParseLambda(parameters, null, "y => y.Name"); + var @delegate = lambdaExpression.Compile(); + var result = @delegate.DynamicInvoke(qry) as IEnumerable; + + result.Should().BeEquivalentTo(new[] { "John", "Doe" }); + } +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 6822ad17..90cb9d6b 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -6,16 +6,19 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; public class SystemTextJsonTests { - private const string ExampleJson = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - } - ]"; + private const string ExampleJson = + """ + [ + { + "Name": "John", + "Age": 30 + }, + { + "Name": "Doe", + "Age": 40 + } + ] + """; private readonly JsonDocument _source = JsonDocument.Parse(ExampleJson); [Fact] @@ -96,17 +99,20 @@ public void Count() [Fact] public void Distinct() { - var json = @"[ - { - ""Name"": ""John"" - }, - { - ""Name"": ""Doe"" - }, - { - ""Name"": ""John"" - } - ]"; + var json = + """ + [ + { + "Name": "John" + }, + { + "Name": "Doe" + }, + { + "Name": "John" + } + ] + """; var source = JsonDocument.Parse(json); // Act @@ -197,20 +203,23 @@ public void OrderBy() [Fact] public void OrderBy_ThenBy() { - var json = @"[ - { - ""Name"": ""John"", - ""Age"": 30 - }, - { - ""Name"": ""Doe"", - ""Age"": 40 - }, - { - ""Name"": ""Stef"", - ""Age"": 18 - } - ]"; + var json = + """ + [ + { + "Name": "John", + "Age": 30 + }, + { + "Name": "Doe", + "Age": 40 + }, + { + "Name": "Stef", + "Age": 18 + } + ] + """; var source = JsonDocument.Parse(json); // Act From b0eccbc6e38185910a65491c89f5129b6c85e31e Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 15 Jun 2024 16:28:14 +0200 Subject: [PATCH 42/53] @ --- .../NewtonsoftJsonTests.cs | 12 ++++++------ .../SystemTextJsonTests.cs | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 38db907d..b1d3e3ef 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -221,7 +221,7 @@ public void OrderBy_ThenBy() [Fact] public void Page() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JArray.Parse(json); // Act @@ -235,7 +235,7 @@ public void Page() [Fact] public void PageResult() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JArray.Parse(json); // Act @@ -267,7 +267,7 @@ public void Select() public void Select_ResultType() { // Arrange - var json = @"[1, 2, 3]"; + var json = "[1, 2, 3]"; var source = JArray.Parse(json); // Act @@ -298,7 +298,7 @@ public void SingleOrDefault() [Fact] public void SkipWhile() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JArray.Parse(json); // Act @@ -312,7 +312,7 @@ public void SkipWhile() [Fact] public void Take() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JArray.Parse(json); // Act @@ -326,7 +326,7 @@ public void Take() [Fact] public void TakeWhile() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JArray.Parse(json); // Act diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 90cb9d6b..a5b0fe75 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -233,7 +233,7 @@ public void OrderBy_ThenBy() [Fact] public void Page() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JsonDocument.Parse(json); // Act @@ -247,7 +247,7 @@ public void Page() [Fact] public void PageResult() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JsonDocument.Parse(json); // Act @@ -293,7 +293,7 @@ public void SingleOrDefault() public void Select_ResultType() { // Arrange - var json = @"[1, 2, 3]"; + var json = "[1, 2, 3]"; var source = JsonDocument.Parse(json); // Act @@ -307,7 +307,7 @@ public void Select_ResultType() [Fact] public void Skip() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JsonDocument.Parse(json); // Act @@ -321,7 +321,7 @@ public void Skip() [Fact] public void SkipWhile() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JsonDocument.Parse(json); // Act @@ -335,7 +335,7 @@ public void SkipWhile() [Fact] public void Take() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JsonDocument.Parse(json); // Act @@ -349,7 +349,7 @@ public void Take() [Fact] public void TakeWhile() { - var json = @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; + var json = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"; var source = JsonDocument.Parse(json); // Act From b59bd3c7453217a5e5fe82455f2e1220c8238e9c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 15 Jun 2024 16:59:34 +0200 Subject: [PATCH 43/53] remove ThenBy --- .../Config/NewtonsoftJsonParsingConfig.cs | 11 +- .../NewtonsoftJsonExtensions.cs | 134 +++++++++--------- .../Config/SystemTextJsonParsingConfig.cs | 8 +- .../SystemTextJsonExtensions.cs | 130 ++++++++--------- .../DynamicQueryableExtensions.cs | 20 +-- .../NewtonsoftJsonTests.cs | 34 +++-- .../SystemTextJsonTests.cs | 30 +++- 7 files changed, 207 insertions(+), 160 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs index 4b0156fd..fad7f9f1 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs @@ -2,9 +2,18 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Config; +/// +/// Configuration class for System.Linq.Dynamic.Core.NewtonsoftJson which implements the . +/// public class NewtonsoftJsonParsingConfig : ParsingConfig { - public static NewtonsoftJsonParsingConfig Default { get; } = new(); + /// + /// The default ParsingConfig for . + /// + public new static NewtonsoftJsonParsingConfig Default { get; } = new(); + /// + /// The default to use. + /// public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 5b34c6c7..9ba04021 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -562,7 +562,7 @@ public static object Min(this JArray source, LambdaExpression lambda) /// A sequence of values to order. /// The . /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JArray OrderBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, params object?[] args) { @@ -579,7 +579,7 @@ public static JArray OrderBy(this JArray source, NewtonsoftJsonParsingConfig con /// /// A sequence of values to order. /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JArray OrderBy(this JArray source, string ordering, params object?[] args) { @@ -593,7 +593,7 @@ public static JArray OrderBy(this JArray source, string ordering, params object? /// The . /// An expression string to indicate values to order by. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JArray OrderBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) { @@ -607,7 +607,7 @@ public static JArray OrderBy(this JArray source, NewtonsoftJsonParsingConfig con /// A sequence of values to order. /// An expression string to indicate values to order by. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JArray OrderBy(this JArray source, string ordering, IComparer comparer, params object?[] args) { @@ -654,7 +654,7 @@ public static PagedResult PageResult(this JArray source, int page, int pageSize, /// /// A sequence of values to project. /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. public static JArray Select(this JArray source, string selector, params object?[] args) { @@ -667,7 +667,7 @@ public static JArray Select(this JArray source, string selector, params object?[ /// A sequence of values to project. /// The . /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. public static JArray Select(this JArray source, NewtonsoftJsonParsingConfig config, string selector, params object?[] args) { @@ -874,67 +874,67 @@ public static JArray TakeWhile(this JArray source, string predicate, params obje } #endregion TakeWhile - #region ThenBy - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// The . - /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, params object?[] args) - { - Check.NotNull(source); - Check.NotNull(config); - - var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable - return ToJArray(() => queryable.ThenBy(ordering, args)); - } - - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// The . - /// An expression string to indicate values to order by. - /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) - { - Check.NotNull(source); - Check.NotNull(config); - - var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable - return ToJArray(() => queryable.ThenBy(ordering, comparer, args)); - } - - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JArray ThenBy(this JArray source, string ordering, params object?[] args) - { - return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, args); - } - - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// An expression string to indicate values to order by. - /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JArray ThenBy(this JArray source, string ordering, IComparer comparer, params object?[] args) - { - return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, comparer, args); - } - #endregion ThenBy + //#region ThenBy + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// The . + ///// An expression string to indicate values to order by. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, params object?[] args) + //{ + // Check.NotNull(source); + // Check.NotNull(config); + + // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable + // return ToJArray(() => queryable.ThenBy(ordering, args)); + //} + + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// The . + ///// An expression string to indicate values to order by. + ///// The comparer to use. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) + //{ + // Check.NotNull(source); + // Check.NotNull(config); + + // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable + // return ToJArray(() => queryable.ThenBy(ordering, comparer, args)); + //} + + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// An expression string to indicate values to order by. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JArray ThenBy(this JArray source, string ordering, params object?[] args) + //{ + // return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, args); + //} + + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// An expression string to indicate values to order by. + ///// The comparer to use. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JArray ThenBy(this JArray source, string ordering, IComparer comparer, params object?[] args) + //{ + // return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, comparer, args); + //} + //#endregion ThenBy #region Where /// diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs index d5ada435..eb42ff6d 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs @@ -1,6 +1,12 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Config; +/// +/// Configuration class for System.Linq.Dynamic.Core.SystemTextJson which implements the . +/// public class SystemTextJsonParsingConfig : ParsingConfig { - public static SystemTextJsonParsingConfig Default { get; } = new(); + /// + /// The default ParsingConfig for . + /// + public new static SystemTextJsonParsingConfig Default { get; } = new(); } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 210f7fbd..4448798e 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -682,7 +682,7 @@ public static object Min(this JsonDocument source, LambdaExpression lambda) /// A sequence of values to order. /// The . /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JsonDocument OrderBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, params object?[] args) { @@ -699,7 +699,7 @@ public static JsonDocument OrderBy(this JsonDocument source, SystemTextJsonParsi /// /// A sequence of values to order. /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JsonDocument OrderBy(this JsonDocument source, string ordering, params object?[] args) { @@ -713,7 +713,7 @@ public static JsonDocument OrderBy(this JsonDocument source, string ordering, pa /// The . /// An expression string to indicate values to order by. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JsonDocument OrderBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) { @@ -727,7 +727,7 @@ public static JsonDocument OrderBy(this JsonDocument source, SystemTextJsonParsi /// A sequence of values to order. /// An expression string to indicate values to order by. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static JsonDocument OrderBy(this JsonDocument source, string ordering, IComparer comparer, params object?[] args) { @@ -1124,67 +1124,67 @@ public static JsonDocument TakeWhile(this JsonDocument source, string predicate, } #endregion TakeWhile - #region ThenBy - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// The . - /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, params object?[] args) - { - Check.NotNull(source); - Check.NotNull(config); - - var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable - return ToJsonDocumentArray(() => queryable.ThenBy(ordering, args)); - } - - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// The . - /// An expression string to indicate values to order by. - /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) - { - Check.NotNull(source); - Check.NotNull(config); - - var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable; - return ToJsonDocumentArray(() => queryable.ThenBy(ordering, comparer, args)); - } - - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JsonDocument ThenBy(this JsonDocument source, string ordering, params object?[] args) - { - return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, args); - } - - /// - /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - /// - /// A sequence of values to order. - /// An expression string to indicate values to order by. - /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - /// A whose elements are sorted according to the specified . - public static JsonDocument ThenBy(this JsonDocument source, string ordering, IComparer comparer, params object?[] args) - { - return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, comparer, args); - } - #endregion ThenBy + //#region ThenBy + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// The . + ///// An expression string to indicate values to order by. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, params object?[] args) + //{ + // Check.NotNull(source); + // Check.NotNull(config); + + // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable + // return ToJsonDocumentArray(() => queryable.ThenBy(ordering, args)); + //} + + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// The . + ///// An expression string to indicate values to order by. + ///// The comparer to use. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) + //{ + // Check.NotNull(source); + // Check.NotNull(config); + + // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable; + // return ToJsonDocumentArray(() => queryable.ThenBy(ordering, comparer, args)); + //} + + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// An expression string to indicate values to order by. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JsonDocument ThenBy(this JsonDocument source, string ordering, params object?[] args) + //{ + // return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, args); + //} + + ///// + ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. + ///// + ///// A sequence of values to order. + ///// An expression string to indicate values to order by. + ///// The comparer to use. + ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + ///// A whose elements are sorted according to the specified . + //public static JsonDocument ThenBy(this JsonDocument source, string ordering, IComparer comparer, params object?[] args) + //{ + // return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, comparer, args); + //} + //#endregion ThenBy #region Where /// diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index b25cb3bf..9accc7eb 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -646,7 +646,7 @@ public static dynamic FirstOrDefault(this IQueryable source, LambdaExpression la /// The . /// A string expression to specify the key for each element. /// A string expression to specify a result value from each group. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A where each element represents a projection over a group and its key. /// /// @@ -669,7 +669,7 @@ public static IQueryable GroupBy(this IQueryable source, ParsingConfig config, s /// A string expression to specify the key for each element. /// A string expression to specify a result value from each group. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A where each element represents a projection over a group and its key. public static IQueryable GroupBy(this IQueryable source, ParsingConfig config, string keySelector, string resultSelector, IEqualityComparer? equalityComparer, object[]? args) { @@ -984,7 +984,7 @@ public static IQueryable GroupJoin(this IQueryable outer, IEnumerable inner, str /// A dynamic function to extract the join key from each element of the first sequence. /// A dynamic function to extract the join key from each element of the second sequence. /// A dynamic function to create a result element from two matching elements. - /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. /// An obtained by performing an inner join on two sequences. public static IQueryable Join(this IQueryable outer, ParsingConfig config, IEnumerable inner, string outerKeySelector, string innerKeySelector, string resultSelector, params object?[] args) { @@ -1043,7 +1043,7 @@ public static IQueryable Join(this IQueryable outer, IEnumerable inner, string o /// A dynamic function to extract the join key from each element of the first sequence. /// A dynamic function to extract the join key from each element of the second sequence. /// A dynamic function to create a result element from two matching elements. - /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. /// This overload only works on elements where both sequences and the resulting element match. /// An that has elements of type TResult obtained by performing an inner join on two sequences. public static IQueryable Join(this IQueryable outer, ParsingConfig config, IEnumerable inner, string outerKeySelector, string innerKeySelector, string resultSelector, params object?[] args) @@ -1532,7 +1532,7 @@ public static IOrderedQueryable OrderBy(this IQueryableA sequence of values to order. /// The . /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . /// /// @@ -1558,7 +1558,7 @@ public static IOrderedQueryable OrderBy(this IQueryable source, ParsingConfig co /// The . /// An expression string to indicate values to order by. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static IOrderedQueryable OrderBy(this IQueryable source, ParsingConfig config, string ordering, IComparer comparer, params object?[] args) { @@ -1743,7 +1743,7 @@ public static IQueryable Reverse(this IQueryable source) /// A sequence of values to project. /// The . /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a projection string on each element of source. /// /// @@ -1867,7 +1867,7 @@ public static IQueryable Select(this IQueryable source, Type resultType, string /// A sequence of values to project. /// The . /// A projection string expression to apply to each element. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// An whose elements are the result of invoking a one-to-many projection function on each element of the input sequence. /// /// @@ -2517,7 +2517,7 @@ public static IOrderedQueryable ThenBy(this IOrderedQueryable< /// A sequence of values to order. /// The . /// An expression string to indicate values to order by. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . /// /// @@ -2539,7 +2539,7 @@ public static IOrderedQueryable ThenBy(this IOrderedQueryable source, ParsingCon /// The . /// An expression string to indicate values to order by. /// The comparer to use. - /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. + /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. /// A whose elements are sorted according to the specified . public static IOrderedQueryable ThenBy(this IOrderedQueryable source, ParsingConfig config, string ordering, IComparer comparer, params object?[] args) { diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index b1d3e3ef..c288fadb 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -173,24 +173,40 @@ public void Min() [Fact] public void OrderBy() { - // Act 1 + // Act var result = _source.OrderBy("Age").Select("Name"); - // Assert 1 + // Assert var array = result.Select(x => x.Value()); array.Should().BeEquivalentTo("John", "Doe"); + } - // Act 1 - var resultAsc = _source.OrderBy("Age", "Asc").Select("Name"); + [Fact] + public void OrderBy_Asc() + { + // Act + var resultAsc = _source.OrderBy("Age asc").Select("Name"); - // Assert 1 + // Assert var arrayAsc = resultAsc.Select(x => x.Value()); arrayAsc.Should().BeEquivalentTo("Doe", "John"); } [Fact] - public void OrderBy_ThenBy() + public void OrderBy_Desc() { + // Act + var resultAsc = _source.OrderBy("Age desc").Select("Name"); + + // Assert + var arrayAsc = resultAsc.Select(x => x.Value()); + arrayAsc.Should().BeEquivalentTo("John", "Doe"); + } + + [Fact] + public void OrderBy_Multiple() + { + // Arrange var json = """ [ @@ -200,7 +216,7 @@ public void OrderBy_ThenBy() }, { "Name": "Doe", - "Age": 40 + "Age": 30 }, { "Name": "Stef", @@ -211,11 +227,11 @@ public void OrderBy_ThenBy() var source = JArray.Parse(json); // Act - var result = source.OrderBy("Age").ThenBy("Name").Select("Name"); + var result = source.OrderBy("Age, Name").Select("Name"); // Assert var array = result.Select(x => x.Value()); - array.Should().BeEquivalentTo("Doe", "John", "Stef"); + array.Should().BeEquivalentTo("Stef", "John", "Doe"); } [Fact] diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index a5b0fe75..46b05c8c 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -185,15 +185,30 @@ public void Min() [Fact] public void OrderBy() { - // Act 1 + // Act var result = _source.OrderBy("Age").Select("Name"); - // Assert 1 + // Assert var array = result.RootElement.EnumerateArray().Select(x => x.GetString()); array.Should().BeEquivalentTo("John", "Doe"); + } + + [Fact] + public void OrderBy_Asc() + { + // Act + var resultAsc = _source.OrderBy("Age asc").Select("Name"); + // Assert + var arrayAsc = resultAsc.RootElement.EnumerateArray().Select(x => x.GetString()); + arrayAsc.Should().BeEquivalentTo("Doe", "John"); + } + + [Fact] + public void OrderBy_Desc() + { // Act 1 - var resultAsc = _source.OrderBy("Age", "Asc").Select("Name"); + var resultAsc = _source.OrderBy("Age desc").Select("Name"); // Assert 1 var arrayAsc = resultAsc.RootElement.EnumerateArray().Select(x => x.GetString()); @@ -201,8 +216,9 @@ public void OrderBy() } [Fact] - public void OrderBy_ThenBy() + public void OrderBy_Multiple() { + // Arrange var json = """ [ @@ -212,7 +228,7 @@ public void OrderBy_ThenBy() }, { "Name": "Doe", - "Age": 40 + "Age": 30 }, { "Name": "Stef", @@ -223,11 +239,11 @@ public void OrderBy_ThenBy() var source = JsonDocument.Parse(json); // Act - var result = source.OrderBy("Age").ThenBy("Name").Select("Name"); + var result = source.OrderBy("Age, Name").Select("Name"); // Assert var array = result.RootElement.EnumerateArray().Select(x => x.GetString()); - array.Should().BeEquivalentTo("Doe", "John", "Stef"); + array.Should().BeEquivalentTo("Stef", "John", "Doe"); } [Fact] From dbc2e4e851c0106e4c0c648d666a7851a3a38d95 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 24 Jun 2024 08:26:32 +0200 Subject: [PATCH 44/53] CI json --- .github/workflows/ci.yml | 5 +++++ test/Directory.Build.props | 12 ++++++++++++ .../EntityFramework.DynamicLinq.Tests.csproj | 9 --------- ...tem.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj | 8 -------- ...tem.Linq.Dynamic.Core.SystemTextJson.Tests.csproj | 6 ------ 5 files changed, 17 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ee9fb34..932a804b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,11 @@ jobs: - name: Run Tests EFCore net8.0 (with Coverage) run: | dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj --configuration Debug -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-efcore.xml + + - name: Run Tests Json .NET 8 (with Coverage) + run: | + dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj --configuration Debug -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-newtonsoftjson.xml + dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj --configuration Debug -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-systemtextjson.xml - name: End analysis on SonarCloud if: github.event_name != 'pull_request' diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 78974861..a91ae8c4 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -7,4 +7,16 @@ GeneratedCodeAttribute opencover + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj b/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj index c7692428..8f6f03d0 100644 --- a/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj +++ b/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj @@ -35,16 +35,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj index fe7bd9b5..4882ff18 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj @@ -15,16 +15,8 @@ - - - all - runtime; build; native; contentfiles; analyzers - - - - all runtime; build; native; contentfiles; analyzers diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj index 0dcf715d..d437f261 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj @@ -11,15 +11,9 @@ - - all - runtime; build; native; contentfiles; analyzers - - - all runtime; build; native; contentfiles; analyzers From 8ed2a6e43deb99ae12fe19718e7c5b6075ae267b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 24 Jun 2024 08:30:06 +0200 Subject: [PATCH 45/53] csproj / --framework net8.0 --- .github/workflows/ci.yml | 4 ++-- .../System.Linq.Dynamic.Core.Tests.Net5.csproj | 7 ------- .../System.Linq.Dynamic.Core.Tests.Net6.csproj | 10 ---------- .../System.Linq.Dynamic.Core.Tests.Net7.csproj | 10 ---------- .../System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj | 8 +------- .../System.Linq.Dynamic.Core.Tests.csproj | 9 --------- 6 files changed, 3 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 932a804b..781c9fff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,8 +68,8 @@ jobs: - name: Run Tests Json .NET 8 (with Coverage) run: | - dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj --configuration Debug -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-newtonsoftjson.xml - dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj --configuration Debug -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-systemtextjson.xml + dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj --configuration Debug --framework net8.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-newtonsoftjson.xml + dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj --configuration Debug --framework net8.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-systemtextjson.xml - name: End analysis on SonarCloud if: github.event_name != 'pull_request' diff --git a/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj b/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj index ffde669a..9fe37a6e 100644 --- a/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj +++ b/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj @@ -2,7 +2,6 @@ net5.0 - 10 System.Linq.Dynamic.Core.Tests full True @@ -12,23 +11,17 @@ - runtime; build; native; contentfiles; analyzers; buildtransitive all - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - diff --git a/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj b/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj index d0b4421b..a43fa00d 100644 --- a/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj +++ b/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj @@ -11,27 +11,17 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - runtime; build; native; contentfiles; analyzers; buildtransitive all - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - diff --git a/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj b/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj index 4ba004a4..39592148 100644 --- a/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj +++ b/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj @@ -12,27 +12,17 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - runtime; build; native; contentfiles; analyzers; buildtransitive all - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - diff --git a/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj b/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj index 2a2b4d0c..f5547534 100644 --- a/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj +++ b/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj @@ -12,15 +12,8 @@ - - all - runtime; build; native; contentfiles; analyzers - - - - all @@ -42,6 +35,7 @@ + diff --git a/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj b/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj index e47b21d8..27adeeac 100644 --- a/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj @@ -11,15 +11,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - runtime; build; native; contentfiles; analyzers; buildtransitive From b37b5157af1e5910b47233880a0c5a661acffcf4 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 24 Jun 2024 08:55:38 +0200 Subject: [PATCH 46/53] more tests --- .../Extensions/JObjectExtensions.cs | 4 +- .../Properties/AssemblyInfo.cs | 3 ++ .../Extensions/JsonDocumentExtensions.cs | 4 +- .../Properties/AssemblyInfo.cs | 3 ++ .../Extensions/JObjectExtensionsTests.cs | 50 +++++++++++++++++++ .../Extensions/JsonDocumentExtensionsTests.cs | 49 ++++++++++++++++++ 6 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/System.Linq.Dynamic.Core.NewtonsoftJson/Properties/AssemblyInfo.cs create mode 100644 src/System.Linq.Dynamic.Core.SystemTextJson/Properties/AssemblyInfo.cs create mode 100644 test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs create mode 100644 test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs index 11cc21e7..e0b27be2 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs @@ -11,9 +11,7 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Extensions; /// internal static class JObjectExtensions { - private class JTokenResolvers : Dictionary> - { - } + private class JTokenResolvers : Dictionary>; private static readonly JTokenResolvers Resolvers = new() { diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..6139c414 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.NewtonsoftJson.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs index 075ba135..7daf15a5 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs @@ -8,9 +8,7 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions; internal static class JsonDocumentExtensions { - private class JTokenResolvers : Dictionary> - { - } + private class JTokenResolvers : Dictionary>; private static readonly JTokenResolvers Resolvers = new() { diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..ac5e7f26 --- /dev/null +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.SystemTextJson.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs new file mode 100644 index 00000000..deba4c56 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs @@ -0,0 +1,50 @@ +using System.Linq.Dynamic.Core.NewtonsoftJson.Extensions; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests.Extensions; + +public class JObjectExtensionsTests +{ + [Fact] + public void ToDynamicClass() + { + // Arrange + var guid = Guid.NewGuid(); + var jObject = new JObject + { + { "Name", "John" }, + { "Age", 30 }, + { "IsMale", true }, + { "DateOfBirth", new DateTime(1990, 1, 1) }, + { "Guid", guid }, + { "Bytes", new byte[] { 1, 2, 3 } }, + { "TimeSpan", new TimeSpan(1, 2, 3) }, + { "Uri", new Uri("http://www.test.com") }, + { "Null", null }, + { "Undefined", JValue.CreateUndefined() }, + { "None", JValue.CreateNull() }, + { "Object", new JObject { { "Name", "Jane" } } }, + { "Array", new JArray { 1, 2, 3 } } + }; + + // Act + var result = jObject.ToDynamicClass(); + + // Assert + result.Should().BeEquivalentTo(new + { + Name = "John", + Age = 30, + IsMale = true, + DateOfBirth = new DateTime(1990, 1, 1), + Guid = guid, + Bytes = new byte[] { 1, 2, 3 }, + TimeSpan = new TimeSpan(1, 2, 3), + Uri = new Uri("http://www.test.com"), + Object = new { Name = "Jane" }, + Array = new[] { 1, 2, 3 } + }); + } +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs new file mode 100644 index 00000000..048f7f3a --- /dev/null +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs @@ -0,0 +1,49 @@ +using System.Linq.Dynamic.Core.SystemTextJson.Extensions; +using System.Text; +using System.Text.Json; +using FluentAssertions; +using Xunit; + +namespace System.Linq.Dynamic.Core.SystemTextJson.Tests.Extensions; + +public class JsonDocumentExtensionsTests +{ + [Fact] + public void ToDynamicClass() + { + // Arrange + var guid = Guid.NewGuid(); + var utf8JsonReader = new Utf8JsonReader(Encoding.UTF8.GetBytes(@"{ + ""Name"": ""John"", + ""Age"": 30, + ""IsMale"": true, + ""IsTest"": false, + ""DateOfBirth"": ""1990-01-01"", + ""Guid"": """ + guid + @""", + ""TimeSpan"": ""01:02:03"", + ""Uri"": ""http://www.test.com"", + ""Null"": null, + ""Object"": { ""Name"": ""Jane"" }, + ""Array"": [1, 2, 3] + }")); + JsonElement? jsonElement = JsonElement.ParseValue(ref utf8JsonReader); + + // Act + var result = jsonElement.ToDynamicClass(); + + // Assert + result.Should().BeEquivalentTo(new + { + Name = "John", + Age = 30, + IsMale = true, + IsTest = false, + DateOfBirth = new DateTime(1990, 1, 1), + Guid = guid, + TimeSpan = "01:02:03", + Uri = "http://www.test.com", + Object = new { Name = "Jane" }, + Array = new[] { 1, 2, 3 } + }); + } +} \ No newline at end of file From ca50f634c9c3524235eda398c11dd11b8f43dd9c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 24 Jun 2024 09:11:48 +0200 Subject: [PATCH 47/53] float --- .../Extensions/JObjectExtensionsTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs index deba4c56..e65e66e5 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/Extensions/JObjectExtensionsTests.cs @@ -16,7 +16,9 @@ public void ToDynamicClass() { { "Name", "John" }, { "Age", 30 }, + { "FloatValue", 123.5f }, { "IsMale", true }, + { "IsTest", false }, { "DateOfBirth", new DateTime(1990, 1, 1) }, { "Guid", guid }, { "Bytes", new byte[] { 1, 2, 3 } }, @@ -37,7 +39,9 @@ public void ToDynamicClass() { Name = "John", Age = 30, + FloatValue = 123.5f, IsMale = true, + IsTest = false, DateOfBirth = new DateTime(1990, 1, 1), Guid = guid, Bytes = new byte[] { 1, 2, 3 }, From fa559ec3dfa921b9d4fa0128c67489f90c3a2e17 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 24 Jun 2024 09:14:39 +0200 Subject: [PATCH 48/53] mv --- .../Extensions/JObjectExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs index e0b27be2..1a7be2f4 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs @@ -53,14 +53,14 @@ private class JTokenResolvers : Dictionary Date: Fri, 12 Jul 2024 22:12:52 +0200 Subject: [PATCH 49/53] ... --- .../System.Linq.Dynamic.Core.SystemTextJson.csproj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj index 55d5ece5..fa93a71d 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -21,11 +21,6 @@ true - - - - - From 9203f6d8fb97fece81d805a93e6cc320e5cb978b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 21 Aug 2024 20:45:53 +0200 Subject: [PATCH 50/53] ... --- System.Linq.Dynamic.Core.sln | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index 2ffd238d..aa40732a 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -969,6 +969,22 @@ Global {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x64.Build.0 = Release|Any CPU {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x86.ActiveCfg = Release|Any CPU {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E}.Release|x86.Build.0 = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|ARM.Build.0 = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|x64.Build.0 = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Debug|x86.Build.0 = Debug|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|Any CPU.Build.0 = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|ARM.ActiveCfg = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|ARM.Build.0 = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|x64.ActiveCfg = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|x64.Build.0 = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|x86.ActiveCfg = Release|Any CPU + {2DE2052F-0A50-40C7-B6FF-52B52386BF9A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From ebde7ca8a580b43a0267453137640222e785814f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 26 Aug 2024 22:27:02 +0200 Subject: [PATCH 51/53] fix --- System.Linq.Dynamic.Core.sln | 4 ++++ .../DynamicClassFactory.cs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index 2be909cd..afae1909 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -1054,6 +1054,10 @@ Global {D8368319-F370-4071-9411-A3DADB234330} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {B01B327C-FC68-49B6-BDE3-A13D0C66DF5C} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {7AFC2836-0F6E-4B0D-8BB3-13317A3B6616} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} + {8C5851B8-5C47-4229-AB55-D4252703598E} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} + {912FBF24-3CAE-4A50-B5EA-E525B9FAEC90} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} + {FA01CE15-315A-499E-AFC2-955CA7EB45FF} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} + {D5844AE4-53FA-4C8A-9D52-AD213FD0CA1E} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38} {2DE2052F-0A50-40C7-B6FF-52B52386BF9A} = {122BC4FA-7563-4E35-9D17-077F16F1629F} {7A31366C-DD98-41A3-A0C1-A97068BD9658} = {BCA2A024-9032-4E56-A6C4-17A15D921728} EndGlobalSection diff --git a/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs b/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs index d4ce7065..0dc61889 100644 --- a/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs +++ b/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs @@ -176,6 +176,24 @@ public static Type CreateType(IList properties, bool createPara return type; } + /// + /// Create an instance of a based on a list of . + /// + /// The dynamic properties including the value you want to set in the generated instance. + /// Create a constructor with parameters. Default set to true. Note that for Linq-to-Database objects, this needs to be set to false. + /// Instance of a . + public static DynamicClass CreateInstance(IList dynamicPropertiesWithValue, bool createParameterCtor = true) + { + var type = CreateType(dynamicPropertiesWithValue.Cast().ToArray(), createParameterCtor); + var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; + foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) + { + dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); + } + + return dynamicClass; + } + private static Type EmitType(IList properties, bool createParameterCtor) { var typeIndex = Interlocked.Increment(ref _index); From 6c7c513e7e622e070daa67cba6b8dfe5061b3d45 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 12 Oct 2024 17:50:09 +0200 Subject: [PATCH 52/53] 7 --- .../System.Linq.Dynamic.Core.NewtonsoftJson.csproj | 2 +- .../System.Linq.Dynamic.Core.SystemTextJson.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj index 7fd1effd..5e3d398e 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj @@ -22,7 +22,7 @@ - + diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj index fa93a71d..4d1e66eb 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/System.Linq.Dynamic.Core.SystemTextJson.csproj @@ -22,7 +22,7 @@ - + From dbaf7601651596e6ced1f8776ee21f92ae50e848 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 3 Nov 2024 19:50:19 +0100 Subject: [PATCH 53/53] remove methods --- .../NewtonsoftJsonExtensions.cs | 232 ---------------- .../SystemTextJsonExtensions.cs | 247 ------------------ .../NewtonsoftJsonTests.cs | 37 ++- ...q.Dynamic.Core.SystemTextJson.Tests.csproj | 1 - .../SystemTextJsonTests.cs | 33 ++- 5 files changed, 64 insertions(+), 486 deletions(-) diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 9ba04021..5b1c69ac 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -105,20 +105,6 @@ public static bool Any(this JArray source, string predicate, params object?[] ar { return Any(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Determines whether a sequence contains any elements. - /// - /// The source - /// A Lambda Expression. - /// true if the source sequence contains any elements; otherwise, false. - public static bool Any(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Any(lambda); - } #endregion Any #region Average @@ -164,21 +150,6 @@ public static double Average(this JArray source, string predicate, params object { return Average(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the average of a sequence of numeric values. - /// - /// The source - /// A Lambda Expression. - /// The average of the values in the sequence. - public static double Average(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - Check.NotNull(lambda); - - var queryable = ToQueryable(source); - return queryable.Average(lambda); - } #endregion Average #region Cast @@ -253,20 +224,6 @@ public static int Count(this JArray source, string predicate, params object?[] a { return Count(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the number of elements in a sequence. - /// - /// The that contains the elements to be counted. - /// A cached Lambda Expression. - /// The number of elements in the specified sequence that satisfies a condition. - public static int Count(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Count(lambda); - } #endregion Count #region DefaultIfEmpty @@ -314,20 +271,6 @@ public static JToken First(this JArray source, string predicate, params object?[ { return First(source, NewtonsoftJsonParsingConfig.Default, predicate, args) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); } - - /// - /// Returns the first element of a sequence that satisfies a specified condition. - /// - /// The to return the first element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JToken First(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJToken(queryable.First(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); - } #endregion First #region FirstOrDefault @@ -359,20 +302,6 @@ public static JToken First(this JArray source, LambdaExpression lambda) { return FirstOrDefault(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. - /// - /// The to return the first element of. - /// A cached Lambda Expression. - /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. - public static JToken? FirstOrDefault(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJToken(queryable.FirstOrDefault(lambda)); - } #endregion FirstOrDefault #region Last @@ -404,20 +333,6 @@ public static JToken Last(this JArray source, string predicate, params object?[] { return Last(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the last element of a sequence that satisfies a specified condition. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JToken Last(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJToken(queryable.Last(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); - } #endregion Last #region LastOrDefault @@ -449,20 +364,6 @@ public static JToken Last(this JArray source, LambdaExpression lambda) { return LastOrDefault(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JToken? LastOrDefault(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJToken(queryable.LastOrDefault(lambda)); - } #endregion LastOrDefault #region Max @@ -494,20 +395,6 @@ public static object Max(this JArray source, string predicate, params object?[] { return Max(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the max element of a sequence. - /// - /// A sequence of values to calculate find the max for. - /// A Lambda Expression. - /// The max element in the sequence. - public static object Max(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Max(lambda); - } #endregion Max #region Min @@ -539,20 +426,6 @@ public static object Min(this JArray source, string predicate, params object?[] { return Min(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the min element of a sequence. - /// - /// A sequence of values to calculate find the min for. - /// A Lambda Expression. - /// The min element in the sequence. - public static object Min(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Min(lambda); - } #endregion Min #region OrderBy @@ -749,19 +622,6 @@ public static JToken Single(this JArray source, string predicate, params object? { return Single(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there - /// is not exactly one element in the sequence. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JToken Single(this JArray source, LambdaExpression lambda) - { - var queryable = ToQueryable(source); - return ToJToken(queryable.Single(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); - } #endregion Single #region SingleOrDefault @@ -795,21 +655,6 @@ public static JToken Single(this JArray source, LambdaExpression lambda) { return SingleOrDefault(source, NewtonsoftJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence - /// is empty; and throws an exception if there is not exactly one element in the sequence. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JToken? SingleOrDefault(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJToken(queryable.SingleOrDefault(lambda)); - } #endregion SingleOrDefault #region SkipWhile @@ -874,68 +719,6 @@ public static JArray TakeWhile(this JArray source, string predicate, params obje } #endregion TakeWhile - //#region ThenBy - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// The . - ///// An expression string to indicate values to order by. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, params object?[] args) - //{ - // Check.NotNull(source); - // Check.NotNull(config); - - // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable - // return ToJArray(() => queryable.ThenBy(ordering, args)); - //} - - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// The . - ///// An expression string to indicate values to order by. - ///// The comparer to use. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JArray ThenBy(this JArray source, NewtonsoftJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) - //{ - // Check.NotNull(source); - // Check.NotNull(config); - - // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable - // return ToJArray(() => queryable.ThenBy(ordering, comparer, args)); - //} - - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// An expression string to indicate values to order by. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JArray ThenBy(this JArray source, string ordering, params object?[] args) - //{ - // return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, args); - //} - - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// An expression string to indicate values to order by. - ///// The comparer to use. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JArray ThenBy(this JArray source, string ordering, IComparer comparer, params object?[] args) - //{ - // return ThenBy(source, NewtonsoftJsonParsingConfig.Default, ordering, comparer, args); - //} - //#endregion ThenBy - #region Where /// /// Filters a sequence of values based on a predicate. @@ -971,21 +754,6 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi var queryable = ToQueryable(source, config); return ToJArray(() => queryable.Where(config, predicate, args)); } - - /// - /// Filters a sequence of values based on a predicate. - /// - /// A to filter. - /// A cached Lambda Expression. - /// A that contains elements from the input sequence that satisfy the condition specified by LambdaExpression. - public static JArray Where(this JArray source, LambdaExpression lambda) - { - Check.NotNull(source); - Check.NotNull(lambda); - - var queryable = ToQueryable(source); - return ToJArray(() => queryable.Where(lambda)); - } #endregion Where #region Private Methods diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs index 4448798e..48ccc581 100644 --- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs @@ -106,20 +106,6 @@ public static bool Any(this JsonDocument source, string predicate, params object { return Any(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Determines whether a sequence contains any elements. - /// - /// The source - /// A Lambda Expression. - /// true if the source sequence contains any elements; otherwise, false. - public static bool Any(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Any(lambda); - } #endregion Any #region Average @@ -165,21 +151,6 @@ public static double Average(this JsonDocument source, string predicate, params { return Average(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the average of a sequence of numeric values. - /// - /// The source - /// A Lambda Expression. - /// The average of the values in the sequence. - public static double Average(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - Check.NotNull(lambda); - - var queryable = ToQueryable(source); - return queryable.Average(lambda); - } #endregion Average #region Cast @@ -267,20 +238,6 @@ public static int Count(this JsonDocument source, string predicate, params objec { return Count(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the number of elements in a sequence. - /// - /// The that contains the elements to be counted. - /// A cached Lambda Expression. - /// The number of elements in the specified sequence that satisfies a condition. - public static int Count(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Count(lambda); - } #endregion Count #region DefaultIfEmpty @@ -369,20 +326,6 @@ public static JsonElement First(this JsonDocument source, string predicate, para { return First(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the first element of a sequence that satisfies a specified condition. - /// - /// The to return the first element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JsonElement First(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJsonElement(queryable.First(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); - } #endregion First #region FirstOrDefault @@ -427,20 +370,6 @@ public static JsonElement First(this JsonDocument source, LambdaExpression lambd { return FirstOrDefault(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. - /// - /// The to return the first element of. - /// A cached Lambda Expression. - /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. - public static JsonElement? FirstOrDefault(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJsonElement(queryable.FirstOrDefault(lambda)); - } #endregion FirstOrDefault #region Last @@ -485,20 +414,6 @@ public static JsonElement Last(this JsonDocument source, string predicate, param { return Last(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the last element of a sequence that satisfies a specified condition. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JsonElement Last(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJsonElement(queryable.Last(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); - } #endregion Last #region LastOrDefault @@ -543,20 +458,6 @@ public static JsonElement Last(this JsonDocument source, LambdaExpression lambda { return LastOrDefault(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JsonElement? LastOrDefault(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJsonElement(queryable.LastOrDefault(lambda)); - } #endregion LastOrDefault #region Max @@ -601,20 +502,6 @@ public static object Max(this JsonDocument source, string predicate, params obje { return Max(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the max element of a sequence. - /// - /// A sequence of values to calculate find the max for. - /// A Lambda Expression. - /// The max element in the sequence. - public static object Max(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Max(lambda); - } #endregion Max #region Min @@ -659,20 +546,6 @@ public static object Min(this JsonDocument source, string predicate, params obje { return Min(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the min element of a sequence. - /// - /// A sequence of values to calculate find the min for. - /// A Lambda Expression. - /// The min element in the sequence. - public static object Min(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return queryable.Min(lambda); - } #endregion Min #region OrderBy @@ -893,19 +766,6 @@ public static JsonElement Single(this JsonDocument source, string predicate, par { return Single(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there - /// is not exactly one element in the sequence. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JsonElement Single(this JsonDocument source, LambdaExpression lambda) - { - var queryable = ToQueryable(source); - return ToJsonElement(queryable.Single(lambda)) ?? throw new InvalidOperationException(Res.SequenceContainsNoElements); - } #endregion Single #region SingleOrDefault @@ -954,21 +814,6 @@ public static JsonElement Single(this JsonDocument source, LambdaExpression lamb { return SingleOrDefault(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence - /// is empty; and throws an exception if there is not exactly one element in the sequence. - /// - /// The to return the last element of. - /// A cached Lambda Expression. - /// The first element in source that passes the test in predicate. - public static JsonElement? SingleOrDefault(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - - var queryable = ToQueryable(source); - return ToJsonElement(queryable.SingleOrDefault(lambda)); - } #endregion SingleOrDefault #region Skip @@ -1060,21 +905,6 @@ public static object Sum(this JsonDocument source, string predicate, params obje { return Sum(source, SystemTextJsonParsingConfig.Default, predicate, args); } - - /// - /// Computes the sum of a sequence of numeric values. - /// - /// A sequence of numeric values to calculate the sum of. - /// A Lambda Expression. - /// The sum of the values in the sequence. - public static object Sum(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - Check.NotNull(lambda); - - var queryable = ToQueryable(source); - return queryable.Sum(lambda); - } #endregion Sum #region Take @@ -1124,68 +954,6 @@ public static JsonDocument TakeWhile(this JsonDocument source, string predicate, } #endregion TakeWhile - //#region ThenBy - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// The . - ///// An expression string to indicate values to order by. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, params object?[] args) - //{ - // Check.NotNull(source); - // Check.NotNull(config); - - // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable - // return ToJsonDocumentArray(() => queryable.ThenBy(ordering, args)); - //} - - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// The . - ///// An expression string to indicate values to order by. - ///// The comparer to use. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JsonDocument ThenBy(this JsonDocument source, SystemTextJsonParsingConfig config, string ordering, IComparer comparer, params object?[] args) - //{ - // Check.NotNull(source); - // Check.NotNull(config); - - // var queryable = ToQueryable(source, config).OrderBy("0"); // Workaround to get IOrderedQueryable; - // return ToJsonDocumentArray(() => queryable.ThenBy(ordering, comparer, args)); - //} - - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// An expression string to indicate values to order by. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JsonDocument ThenBy(this JsonDocument source, string ordering, params object?[] args) - //{ - // return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, args); - //} - - ///// - ///// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. - ///// - ///// A sequence of values to order. - ///// An expression string to indicate values to order by. - ///// The comparer to use. - ///// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. - ///// A whose elements are sorted according to the specified . - //public static JsonDocument ThenBy(this JsonDocument source, string ordering, IComparer comparer, params object?[] args) - //{ - // return ThenBy(source, SystemTextJsonParsingConfig.Default, ordering, comparer, args); - //} - //#endregion ThenBy - #region Where /// /// Filters a sequence of values based on a predicate. @@ -1215,21 +983,6 @@ public static JsonDocument Where(this JsonDocument source, SystemTextJsonParsing var queryable = ToQueryable(source, config); return ToJsonDocumentArray(() => queryable.Where(config, predicate, args)); } - - /// - /// Filters a sequence of values based on a predicate. - /// - /// A to filter. - /// A cached Lambda Expression. - /// A that contains elements from the input sequence that satisfy the condition specified by LambdaExpression. - public static JsonDocument Where(this JsonDocument source, LambdaExpression lambda) - { - Check.NotNull(source); - Check.NotNull(lambda); - - var queryable = ToQueryable(source); - return ToJsonDocumentArray(() => queryable.Where(lambda)); - } #endregion Where #region Private Methods diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index c288fadb..debfb4b7 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -6,7 +6,7 @@ namespace System.Linq.Dynamic.Core.NewtonsoftJson.Tests; public class NewtonsoftJsonTests { - private const string ExampleJson = + private const string ExampleJsonObjectArray = """ [ { @@ -19,7 +19,16 @@ public class NewtonsoftJsonTests } ] """; - private readonly JArray _source = JArray.Parse(ExampleJson); + private readonly JArray _source = JArray.Parse(ExampleJsonObjectArray); + + private const string ExampleJsonIntArray = + """ + [ + 30, + 40 + ] + """; + private readonly JArray _sourceIntArray = JArray.Parse(ExampleJsonIntArray); [Fact] public void Aggregate() @@ -43,6 +52,16 @@ public void All() [Fact] public void Any() + { + // Act + var result = _source.Any(); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public void Any_Predicate() { // Act var result = _source.Any("Age > 20"); @@ -53,6 +72,16 @@ public void Any() [Fact] public void Average() + { + // Act + var result = _sourceIntArray.Average(); + + // Assert + result.Should().BeApproximately(35, 0.00001); + } + + [Fact] + public void Average_Predicate() { // Act var result = _source.Average("Age"); @@ -84,7 +113,7 @@ public void Cast() public void Count() { // Act 1 - var result1 = _source.Count(); + var result1 = _source.Count; // Assert 1 result1.Should().Be(2); @@ -143,7 +172,7 @@ public void FirstOrDefault() public void Last() { // Act + Assert - ((string?)_source.First("Age > 30")["Name"]).Should().Be("Doe"); + ((string?)_source.Last("Age > 30")["Name"]).Should().Be("Doe"); } [Fact] diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj index d437f261..dd619154 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj @@ -12,7 +12,6 @@ - all diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index 46b05c8c..ca337755 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -6,7 +6,7 @@ namespace System.Linq.Dynamic.Core.SystemTextJson.Tests; public class SystemTextJsonTests { - private const string ExampleJson = + private const string ExampleJsonObjectArray = """ [ { @@ -19,7 +19,16 @@ public class SystemTextJsonTests } ] """; - private readonly JsonDocument _source = JsonDocument.Parse(ExampleJson); + private readonly JsonDocument _source = JsonDocument.Parse(ExampleJsonObjectArray); + + private const string ExampleJsonIntArray = + """ + [ + 30, + 40 + ] + """; + private readonly JsonDocument _sourceIntArray = JsonDocument.Parse(ExampleJsonIntArray); [Fact] public void Aggregate() @@ -43,6 +52,16 @@ public void All() [Fact] public void Any() + { + // Act + var result = _source.Any(); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public void Any_Predicate() { // Act var result = _source.Any("Age > 20"); @@ -53,6 +72,16 @@ public void Any() [Fact] public void Average() + { + // Act + var result = _sourceIntArray.Average(); + + // Assert + result.Should().BeApproximately(35, 0.00001); + } + + [Fact] + public void Average_Predicate() { // Act var result = _source.Average("Age");