From 163487ef30ce985409d885d6a02b95cab5fd766a Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Wed, 21 Feb 2024 14:14:31 +0200 Subject: [PATCH 01/15] Add System.Text.Json ref, bump 4x target fw, arrange Newtonsoft refs --- .../DevExtreme.AspNet.Data.Tests.Common.csproj | 2 +- .../DevExtreme.AspNet.Data.Tests.NET4.csproj | 2 -- .../DevExtreme.AspNet.Data.csproj | 16 ++++++++++------ net/Sample/Sample.csproj | 1 + 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests.Common/DevExtreme.AspNet.Data.Tests.Common.csproj b/net/DevExtreme.AspNet.Data.Tests.Common/DevExtreme.AspNet.Data.Tests.Common.csproj index 345163de..7a31e610 100644 --- a/net/DevExtreme.AspNet.Data.Tests.Common/DevExtreme.AspNet.Data.Tests.Common.csproj +++ b/net/DevExtreme.AspNet.Data.Tests.Common/DevExtreme.AspNet.Data.Tests.Common.csproj @@ -1,7 +1,7 @@ - net452;net6.0 + net461;net6.0 DevExtreme.AspNet.Data.Tests False diff --git a/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj b/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj index b996ba11..aaaa43d7 100644 --- a/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj +++ b/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj @@ -11,8 +11,6 @@ - - diff --git a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj index f047ca60..cc240320 100644 --- a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj +++ b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj @@ -14,17 +14,13 @@ DevExtreme.AspNet.Data 99.0 - net452;net6.0 + net461;net6.0 full bin\Debug\$(TargetFramework)\DevExtreme.AspNet.Data.xml 1591 - - - - - + @@ -32,6 +28,14 @@ + + + + + + + + diff --git a/net/Sample/Sample.csproj b/net/Sample/Sample.csproj index e9168b07..6cc8e548 100644 --- a/net/Sample/Sample.csproj +++ b/net/Sample/Sample.csproj @@ -8,6 +8,7 @@ + From a8b259b11082a7198273721a3962d3f4901c5f35 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Wed, 21 Feb 2024 17:05:59 +0200 Subject: [PATCH 02/15] JsonConvert SerializeObject / DeserializeObject -> JsonSerializer Serialize / Deserialize --- .../CustomFilterCompilersTests.cs | 4 ++-- .../DataSourceLoadOptionsParserTests.cs | 2 -- .../DynamicBindingTests.cs | 13 +++++++---- .../FilterExpressionCompilerTests.cs | 9 ++++---- ...erExpressionCompilerTypeConversionTests.cs | 16 ++++++------- .../PaginateViaPrimaryKeyTests.cs | 6 ++--- .../ResponseModelTests.cs | 9 +++----- .../Helpers/DataSourceLoadOptionsParser.cs | 23 ++++++++----------- 8 files changed, 38 insertions(+), 44 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs b/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs index 04d8c80d..bca232a4 100644 --- a/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs @@ -1,10 +1,10 @@ using DevExtreme.AspNet.Data.Helpers; -using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Text.Json; using Xunit; namespace DevExtreme.AspNet.Data.Tests { @@ -34,7 +34,7 @@ public void OneToManyContains() { var source = new[] { new Category(), new Category() }; source[0].Products.Add(new Product { Name = "Chai" }); - var filter = JsonConvert.DeserializeObject(@"[ ""Products"", ""Contains"", ""ch"" ]"); + var filter = JsonSerializer.Deserialize(@"[ ""Products"", ""Contains"", ""ch"" ]"); var loadOptions = new SampleLoadOptions { Filter = filter, diff --git a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs index 5f918942..876e1cd0 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs @@ -1,8 +1,6 @@ using DevExtreme.AspNet.Data.Helpers; -using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Xunit; namespace DevExtreme.AspNet.Data.Tests { diff --git a/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs b/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs index 4fbe2123..e4da3414 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs @@ -1,12 +1,10 @@ using DevExtreme.AspNet.Data.ResponseModel; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Linq; -using System.Text; +using System.Text.Json; using Xunit; namespace DevExtreme.AspNet.Data.Tests { @@ -77,7 +75,8 @@ public void Filter_JValueNull() { CreateExpando(null) }, new SampleLoadOptions { - Filter = new object[] { P1, JValue.CreateNull() } + //TODO: + //Filter = new object[] { P1, JValue.CreateNull() } } ).data); @@ -147,9 +146,10 @@ public void Grouping() { Assert.Equal(4m, expandoResult[1].summary[0]); } + /* [Fact] public void JArray() { - var sourceData = JsonConvert.DeserializeObject(@"[ + var sourceData = JsonSerializer.Deserialize(@"[ { ""p1"": 2 }, { ""p1"": 3 }, { ""p1"": 1 }, @@ -167,6 +167,7 @@ public void JArray() { Assert.Equal(4m, result.summary[0]); } + */ [Fact] public void T598818() { @@ -213,6 +214,7 @@ public void Issue227() { Assert.Single(loadResult.data); } + /* [Fact] public void NoToStringForNumbers() { var compiler = new FilterExpressionCompiler(typeof(object), false); @@ -229,6 +231,7 @@ void Case(IList clientFilter, string expectedExpr, object trueTestValue) { 10 ); } + */ [Fact] public void T714342() { diff --git a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs index 69a67282..f0ca719f 100644 --- a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs @@ -1,10 +1,9 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; +using System.Text.Json; using Xunit; namespace DevExtreme.AspNet.Data.Tests { @@ -144,7 +143,7 @@ public void Not() { [Fact] public void IsUnaryWithJsonCriteria() { - var crit = JsonConvert.DeserializeObject("[\"!\", []]"); + var crit = JsonSerializer.Deserialize("[\"!\", []]"); var compiler = new FilterExpressionCompiler(typeof(object), false); Assert.True(compiler.IsUnary(crit)); } @@ -241,7 +240,7 @@ public void T105740() { [Fact] public void JsonObjects() { - var crit = (IList)JsonConvert.DeserializeObject(@"[ [ ""StringProp"", ""abc"" ], [ ""NullableProp"", null ] ]"); + var crit = JsonSerializer.Deserialize(@"[ [ ""StringProp"", ""abc"" ], [ ""NullableProp"", null ] ]"); var expr = Compile(crit); Assert.Equal(@"((obj.StringProp == ""abc"") AndAlso (obj.NullableProp == null))", expr.Body.ToString()); } diff --git a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs index bb1fbe6b..258ad286 100644 --- a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs @@ -1,9 +1,6 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Text.Json; using Xunit; namespace DevExtreme.AspNet.Data.Tests { @@ -225,15 +222,17 @@ public void StringFuncOnTimeSpan() { AssertEvaluation(obj, new[] { "NullableTime", "contains", "23" }); } + /* [Theory] [InlineData(DateParseHandling.None)] [InlineData(DateParseHandling.DateTime)] [InlineData(DateParseHandling.DateTimeOffset)] public void Issue477(DateParseHandling dateParseHandling) { var date = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero); - var filterJSON = JsonConvert.SerializeObject(new object[] { "this", date }); - var deserializedFilter = JsonConvert.DeserializeObject(filterJSON, new JsonSerializerSettings { - DateParseHandling = dateParseHandling + var filterJSON = JsonSerializer.Serialize(new object[] { "this", date }); + var deserializedFilter = JsonSerializer.Deserialize(filterJSON, new JsonSerializerOptions { + //TODO: + //DateParseHandling = dateParseHandling }); var loadOptions = new SampleLoadOptions { @@ -243,6 +242,7 @@ public void Issue477(DateParseHandling dateParseHandling) { var loadResult = DataSourceLoader.Load(new[] { date }, loadOptions); Assert.Single(loadResult.data); } + */ } } diff --git a/net/DevExtreme.AspNet.Data.Tests/PaginateViaPrimaryKeyTests.cs b/net/DevExtreme.AspNet.Data.Tests/PaginateViaPrimaryKeyTests.cs index 83a780c7..374fd587 100644 --- a/net/DevExtreme.AspNet.Data.Tests/PaginateViaPrimaryKeyTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/PaginateViaPrimaryKeyTests.cs @@ -1,6 +1,6 @@ -using Newtonsoft.Json; -using System; +using System; using System.Linq; +using System.Text.Json; using Xunit; namespace DevExtreme.AspNet.Data.Tests { @@ -133,7 +133,7 @@ public void Bug349() { } static string DataToString(object data) { - return JsonConvert.SerializeObject(data).Replace("\"", ""); + return JsonSerializer.Serialize(data).Replace("\"", ""); } } diff --git a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs index 647be90a..140204e9 100644 --- a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs @@ -1,8 +1,5 @@ using DevExtreme.AspNet.Data.ResponseModel; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; +using System.Text.Json; using Xunit; namespace DevExtreme.AspNet.Data.Tests { @@ -13,13 +10,13 @@ public class ResponseModelTests { public void EmptyLoadResultSerialization() { Assert.Equal( "{\"data\":null}", - JsonConvert.SerializeObject(new LoadResult()) + JsonSerializer.Serialize(new LoadResult()) ); } [Fact] public void EmptyGroupSerialization() { - var json = JsonConvert.SerializeObject(new Group()); + var json = JsonSerializer.Serialize(new Group()); // these must always be present Assert.Contains("\"key\":", json); diff --git a/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs b/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs index e9aef280..066686d5 100644 --- a/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs +++ b/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs @@ -1,10 +1,6 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Text.Json; namespace DevExtreme.AspNet.Data.Helpers { @@ -59,25 +55,26 @@ public static void Parse(DataSourceLoadOptionsBase loadOptions, Func(sort); + loadOptions.Sort = JsonSerializer.Deserialize(sort); if(!String.IsNullOrEmpty(group)) - loadOptions.Group = JsonConvert.DeserializeObject(group); + loadOptions.Group = JsonSerializer.Deserialize(group); if(!String.IsNullOrEmpty(filter)) { - loadOptions.Filter = JsonConvert.DeserializeObject(filter, new JsonSerializerSettings { - DateParseHandling = DateParseHandling.None + loadOptions.Filter = JsonSerializer.Deserialize(filter, new JsonSerializerOptions { + //TODO: + //DateParseHandling = DateParseHandling.None }); } if(!String.IsNullOrEmpty(totalSummary)) - loadOptions.TotalSummary = JsonConvert.DeserializeObject(totalSummary); + loadOptions.TotalSummary = JsonSerializer.Deserialize(totalSummary); if(!String.IsNullOrEmpty(groupSummary)) - loadOptions.GroupSummary = JsonConvert.DeserializeObject(groupSummary); + loadOptions.GroupSummary = JsonSerializer.Deserialize(groupSummary); if(!String.IsNullOrEmpty(select)) - loadOptions.Select = JsonConvert.DeserializeObject(select); + loadOptions.Select = JsonSerializer.Deserialize(select); } } From ec1989f96c24a186e78779ea534ab9af056668d7 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Wed, 21 Feb 2024 17:14:57 +0200 Subject: [PATCH 03/15] JsonProperty DefaultValueHandling.Ignore -> JsonIgnore (DefaultIgnoreCondition?) --- net/DevExtreme.AspNet.Data/ResponseModel/Group.cs | 11 ++++------- .../ResponseModel/LoadResult.cs | 15 +++++---------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs b/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs index ad974550..268963ab 100644 --- a/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs +++ b/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs @@ -1,8 +1,5 @@ -using Newtonsoft.Json; -using System; -using System.Collections; -using System.Linq; -using System.Threading.Tasks; +using System.Collections; +using System.Text.Json.Serialization; namespace DevExtreme.AspNet.Data.ResponseModel { @@ -23,13 +20,13 @@ public class Group { /// /// The count of items in the group. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore]//TODO: DefaultIgnoreCondition? public int? count { get; set; } /// /// Group summary calculation results. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore]//TODO: DefaultIgnoreCondition? public object[] summary { get; set; } } diff --git a/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs b/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs index aff75c50..9300589e 100644 --- a/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs +++ b/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs @@ -1,10 +1,5 @@ -using Newtonsoft.Json; -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Threading.Tasks; +using System.Collections; +using System.Text.Json.Serialization; namespace DevExtreme.AspNet.Data.ResponseModel { @@ -20,19 +15,19 @@ public class LoadResult { /// /// The total number of data objects in the resulting dataset. /// - [DefaultValue(-1), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore]//TODO: DefaultIgnoreCondition? public int totalCount { get; set; } = -1; /// /// The number of top-level groups in the resulting dataset. /// - [DefaultValue(-1), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore]//TODO: DefaultIgnoreCondition? public int groupCount { get; set; } = -1; /// /// Total summary calculation results. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore]//TODO: DefaultIgnoreCondition? public object[] summary { get; set; } } From 0b7d76f2038490e2645559c5b0d6daf85b9dab2e Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Wed, 21 Feb 2024 17:20:50 +0200 Subject: [PATCH 04/15] Build without Newtonsoft ref --- .../GroupHelperTests.cs | 3 --- .../DevExtreme.AspNet.Data.csproj | 4 ---- net/DevExtreme.AspNet.Data/Utils.cs | 9 ++++----- net/Sample/Controllers/NorthwindController.cs | 18 +++++++++--------- net/Sample/DataSourceLoadOptions.cs | 4 ---- net/Sample/Sample.csproj | 1 - 6 files changed, 13 insertions(+), 26 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests/GroupHelperTests.cs b/net/DevExtreme.AspNet.Data.Tests/GroupHelperTests.cs index 9c023527..3d8987b1 100644 --- a/net/DevExtreme.AspNet.Data.Tests/GroupHelperTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/GroupHelperTests.cs @@ -1,10 +1,7 @@ using DevExtreme.AspNet.Data.Helpers; using DevExtreme.AspNet.Data.ResponseModel; -using Newtonsoft.Json; using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace DevExtreme.AspNet.Data.Tests { diff --git a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj index cc240320..2727b64c 100644 --- a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj +++ b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj @@ -28,10 +28,6 @@ - - - - diff --git a/net/DevExtreme.AspNet.Data/Utils.cs b/net/DevExtreme.AspNet.Data/Utils.cs index e2c949c4..8f423bf7 100644 --- a/net/DevExtreme.AspNet.Data/Utils.cs +++ b/net/DevExtreme.AspNet.Data/Utils.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; using System.Linq; -using System.Threading.Tasks; using System.Reflection; -using System.Globalization; -using System.ComponentModel; -using Newtonsoft.Json.Linq; namespace DevExtreme.AspNet.Data { @@ -118,9 +116,10 @@ public static int DynamicCompare(object selectorResult, object clientValue, bool } public static object UnwrapNewtonsoftValue(object value) { + /* if(value is JValue jValue) return jValue.Value; - + */ return value; } diff --git a/net/Sample/Controllers/NorthwindController.cs b/net/Sample/Controllers/NorthwindController.cs index f22cdea9..1b86ed1d 100644 --- a/net/Sample/Controllers/NorthwindController.cs +++ b/net/Sample/Controllers/NorthwindController.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using DevExtreme.AspNet.Data; using Microsoft.AspNetCore.Mvc; -using Sample.Models; -using DevExtreme.AspNet.Data; -using Newtonsoft.Json; using Microsoft.EntityFrameworkCore; +using Sample.Models; +using System.Linq; +using System.Threading.Tasks; namespace Sample.Controllers { @@ -80,7 +77,8 @@ public async Task UpdateOrder(int key, string values) { if(order == null) return StatusCode(409, "Order not found"); - JsonConvert.PopulateObject(values, order); + //TODO: + //JsonConvert.PopulateObject(values, order); if(!TryValidateModel(order)) return BadRequest(ModelState.ToFullErrorString()); @@ -93,7 +91,9 @@ public async Task UpdateOrder(int key, string values) { [HttpPost("insert-order")] public async Task InsertOrder(string values) { var order = new Order(); - JsonConvert.PopulateObject(values, order); + + //TODO: + //JsonConvert.PopulateObject(values, order); if(!TryValidateModel(order)) return BadRequest(ModelState.ToFullErrorString()); diff --git a/net/Sample/DataSourceLoadOptions.cs b/net/Sample/DataSourceLoadOptions.cs index f88d56d9..0c41477b 100644 --- a/net/Sample/DataSourceLoadOptions.cs +++ b/net/Sample/DataSourceLoadOptions.cs @@ -2,10 +2,6 @@ using DevExtreme.AspNet.Data.Helpers; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Newtonsoft.Json; -using System; -using System.Collections; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/net/Sample/Sample.csproj b/net/Sample/Sample.csproj index 6cc8e548..e9168b07 100644 --- a/net/Sample/Sample.csproj +++ b/net/Sample/Sample.csproj @@ -8,7 +8,6 @@ - From 56b5cdf271d83a4fea63037647b76d94521d036b Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Thu, 22 Feb 2024 15:01:27 +0200 Subject: [PATCH 05/15] Add Newtonsoft src ref --- net/DevExtreme.AspNet.Data.sln | 6 ++++++ net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/net/DevExtreme.AspNet.Data.sln b/net/DevExtreme.AspNet.Data.sln index da73d43a..b8c2806a 100644 --- a/net/DevExtreme.AspNet.Data.sln +++ b/net/DevExtreme.AspNet.Data.sln @@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevExtreme.AspNet.Data.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevExtreme.AspNet.Data.Tests.EFCore8", "DevExtreme.AspNet.Data.Tests.EFCore8\DevExtreme.AspNet.Data.Tests.EFCore8.csproj", "{CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Newtonsoft.Json", "..\..\..\JamesNK\Newtonsoft.Json\Src\Newtonsoft.Json\Newtonsoft.Json.csproj", "{F5EB1902-0BA0-4930-A965-E6C947999CD6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -100,6 +102,10 @@ Global {CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}.Release|Any CPU.Build.0 = Release|Any CPU + {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj index 2727b64c..c209eafa 100644 --- a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj +++ b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj @@ -32,6 +32,10 @@ + + + + From 9eee5c65684e5a046404120972ca6d8c288ff85f Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Thu, 22 Feb 2024 15:48:54 +0200 Subject: [PATCH 06/15] Deserialize Info classes with "Web" behavior (otherwise, requires System.Text.Json.Serialization.JsonPropertyName attr on class properties) --- .../Helpers/DataSourceLoadOptionsParser.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs b/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs index 066686d5..645465a0 100644 --- a/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs +++ b/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs @@ -8,6 +8,8 @@ namespace DevExtreme.AspNet.Data.Helpers { /// A parser for the data processing settings. /// public static class DataSourceLoadOptionsParser { + static readonly JsonSerializerOptions DEFAULT_SERIALIZER_OPTIONS = new JsonSerializerOptions(JsonSerializerDefaults.Web); + public const string KEY_REQUIRE_TOTAL_COUNT = "requireTotalCount", KEY_REQUIRE_GROUP_COUNT = "requireGroupCount", @@ -55,10 +57,10 @@ public static void Parse(DataSourceLoadOptionsBase loadOptions, Func(sort); + loadOptions.Sort = JsonSerializer.Deserialize(sort, DEFAULT_SERIALIZER_OPTIONS); if(!String.IsNullOrEmpty(group)) - loadOptions.Group = JsonSerializer.Deserialize(group); + loadOptions.Group = JsonSerializer.Deserialize(group, DEFAULT_SERIALIZER_OPTIONS); if(!String.IsNullOrEmpty(filter)) { loadOptions.Filter = JsonSerializer.Deserialize(filter, new JsonSerializerOptions { @@ -68,10 +70,10 @@ public static void Parse(DataSourceLoadOptionsBase loadOptions, Func(totalSummary); + loadOptions.TotalSummary = JsonSerializer.Deserialize(totalSummary, DEFAULT_SERIALIZER_OPTIONS); if(!String.IsNullOrEmpty(groupSummary)) - loadOptions.GroupSummary = JsonSerializer.Deserialize(groupSummary); + loadOptions.GroupSummary = JsonSerializer.Deserialize(groupSummary, DEFAULT_SERIALIZER_OPTIONS); if(!String.IsNullOrEmpty(select)) loadOptions.Select = JsonSerializer.Deserialize(select); From 65bdf31827d5bf9537e6676b50f4e4a459b26d66 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Thu, 22 Feb 2024 15:52:22 +0200 Subject: [PATCH 07/15] Temporarily disable Parser Filter tests --- .../DataSourceLoadOptionsParserTests.cs | 5 ++++- .../FilterExpressionCompilerTests.cs | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs index 876e1cd0..6886681a 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs @@ -34,7 +34,7 @@ public void AllKeys() { Assert.Equal("foo", opts.Sort[0].Selector); Assert.True(opts.Sort[0].Desc); Assert.Equal("g", opts.Group[0].Selector); - Assert.Equal(new[] { "foo", "bar" }, opts.Filter.Cast()); + //Assert.Equal(new[] { "foo", "bar" }, opts.Filter.Cast());//TODO: Assert.Equal("total", opts.TotalSummary[0].Selector); Assert.Equal("min", opts.TotalSummary[0].SummaryType); @@ -45,10 +45,12 @@ public void AllKeys() { Assert.Equal("f1", opts.Select[0]); } + /* [Fact] public void MustNotParseDates() { var opts = new SampleLoadOptions(); + //TODO: DataSourceLoadOptionsParser.Parse(opts, key => { if(key == DataSourceLoadOptionsParser.KEY_FILTER) return @"[ ""d"", ""2011-12-13T14:15:16Z"" ]"; @@ -57,6 +59,7 @@ public void MustNotParseDates() { Assert.IsType(opts.Filter[1]); } + */ } diff --git a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs index f0ca719f..d1e7715c 100644 --- a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs @@ -238,12 +238,15 @@ public void T105740() { Assert.True((bool)Compile(new object[] { "Date", "12/13/2011 00:00:00" }).Compile().DynamicInvoke(data[0])); } + /* [Fact] public void JsonObjects() { + //TODO: var crit = JsonSerializer.Deserialize(@"[ [ ""StringProp"", ""abc"" ], [ ""NullableProp"", null ] ]"); var expr = Compile(crit); Assert.Equal(@"((obj.StringProp == ""abc"") AndAlso (obj.NullableProp == null))", expr.Body.ToString()); } + */ [Fact] public void StringInequality() { From 7e9864ecd3a4c28c22753ed2b7ef1945e17de160 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Thu, 22 Feb 2024 20:37:51 +0200 Subject: [PATCH 08/15] JsonIgnore with Condition, but does not handle explicit default value, ref ec1989f96c24a186e78779ea534ab9af056668d7 --- net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs | 5 +++++ net/DevExtreme.AspNet.Data/ResponseModel/Group.cs | 4 ++-- net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs | 7 ++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs index 140204e9..cd01e0c1 100644 --- a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs @@ -8,6 +8,9 @@ public class ResponseModelTests { [Fact] public void EmptyLoadResultSerialization() { + //https://github.com/dotnet/runtime/issues/41630 + //https://github.com/dotnet/runtime/issues/36236 + //TODO: Assert.Equal( "{\"data\":null}", JsonSerializer.Serialize(new LoadResult()) @@ -16,6 +19,7 @@ public void EmptyLoadResultSerialization() { [Fact] public void EmptyGroupSerialization() { + //TODO: var json = JsonSerializer.Serialize(new Group()); // these must always be present @@ -29,6 +33,7 @@ public void EmptyGroupSerialization() { #if NET4 [Fact] public void JavaScriptSerializer() { + //TODO: var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); var loadResultJson = serializer.Serialize(new LoadResult()); diff --git a/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs b/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs index 268963ab..cd396222 100644 --- a/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs +++ b/net/DevExtreme.AspNet.Data/ResponseModel/Group.cs @@ -20,13 +20,13 @@ public class Group { /// /// The count of items in the group. /// - [JsonIgnore]//TODO: DefaultIgnoreCondition? + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int? count { get; set; } /// /// Group summary calculation results. /// - [JsonIgnore]//TODO: DefaultIgnoreCondition? + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public object[] summary { get; set; } } diff --git a/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs b/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs index 9300589e..38ad17f4 100644 --- a/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs +++ b/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.ComponentModel; using System.Text.Json.Serialization; namespace DevExtreme.AspNet.Data.ResponseModel { @@ -15,19 +16,19 @@ public class LoadResult { /// /// The total number of data objects in the resulting dataset. /// - [JsonIgnore]//TODO: DefaultIgnoreCondition? + [DefaultValue(-1), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int totalCount { get; set; } = -1; /// /// The number of top-level groups in the resulting dataset. /// - [JsonIgnore]//TODO: DefaultIgnoreCondition? + [DefaultValue(-1), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int groupCount { get; set; } = -1; /// /// Total summary calculation results. /// - [JsonIgnore]//TODO: DefaultIgnoreCondition? + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public object[] summary { get; set; } } From f1f77f43fa392c49eabc86857209762ab85c0fec Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Thu, 29 Feb 2024 23:13:21 +0200 Subject: [PATCH 09/15] Parser: Unwrap expected IList Filter structure from deserialized list (without custom IList converter), isolate true NEWTONSOFT_TESTS (all passed) --- .../CustomFilterCompilersTests.cs | 4 +- .../DataSourceLoadOptionsParserTests.cs | 6 +- .../DynamicBindingTests.cs | 22 ++++--- .../FilterExpressionCompilerTests.cs | 9 ++- ...erExpressionCompilerTypeConversionTests.cs | 21 ++++--- .../Helpers/DataSourceLoadOptionsParser.cs | 6 +- net/DevExtreme.AspNet.Data/Utils.cs | 57 +++++++++++++++++-- 7 files changed, 91 insertions(+), 34 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs b/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs index bca232a4..a9f8c7e8 100644 --- a/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/CustomFilterCompilersTests.cs @@ -1,4 +1,5 @@ using DevExtreme.AspNet.Data.Helpers; + using System; using System.Collections; using System.Collections.Generic; @@ -34,7 +35,8 @@ public void OneToManyContains() { var source = new[] { new Category(), new Category() }; source[0].Products.Add(new Product { Name = "Chai" }); - var filter = JsonSerializer.Deserialize(@"[ ""Products"", ""Contains"", ""ch"" ]"); + var deserializedList = JsonSerializer.Deserialize(@"[ ""Products"", ""Contains"", ""ch"" ]"); + var filter = Compatibility.UnwrapList(deserializedList); var loadOptions = new SampleLoadOptions { Filter = filter, diff --git a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs index 6886681a..7b70fa8c 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs @@ -1,4 +1,5 @@ using DevExtreme.AspNet.Data.Helpers; + using System.Collections.Generic; using System.Linq; using Xunit; @@ -34,7 +35,7 @@ public void AllKeys() { Assert.Equal("foo", opts.Sort[0].Selector); Assert.True(opts.Sort[0].Desc); Assert.Equal("g", opts.Group[0].Selector); - //Assert.Equal(new[] { "foo", "bar" }, opts.Filter.Cast());//TODO: + Assert.Equal(new[] { "foo", "bar" }, opts.Filter.Cast()); Assert.Equal("total", opts.TotalSummary[0].Selector); Assert.Equal("min", opts.TotalSummary[0].SummaryType); @@ -45,12 +46,10 @@ public void AllKeys() { Assert.Equal("f1", opts.Select[0]); } - /* [Fact] public void MustNotParseDates() { var opts = new SampleLoadOptions(); - //TODO: DataSourceLoadOptionsParser.Parse(opts, key => { if(key == DataSourceLoadOptionsParser.KEY_FILTER) return @"[ ""d"", ""2011-12-13T14:15:16Z"" ]"; @@ -59,7 +58,6 @@ public void MustNotParseDates() { Assert.IsType(opts.Filter[1]); } - */ } diff --git a/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs b/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs index e4da3414..06a51cce 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs @@ -1,12 +1,17 @@ using DevExtreme.AspNet.Data.ResponseModel; + using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Linq; -using System.Text.Json; using Xunit; +#if NEWTONSOFT_TESTS +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +#endif + namespace DevExtreme.AspNet.Data.Tests { public class DynamicBindingTests { @@ -68,6 +73,7 @@ public void Filter() { Assert.Equal(2d, expandoResult[0][P1]); } + #if NEWTONSOFT_TESTS [Fact] public void Filter_JValueNull() { var result = ToDictArray(DataSourceLoader.Load( @@ -75,13 +81,13 @@ public void Filter_JValueNull() { CreateExpando(null) }, new SampleLoadOptions { - //TODO: - //Filter = new object[] { P1, JValue.CreateNull() } + Filter = new object[] { P1, JValue.CreateNull() } } ).data); Assert.Single(result); } + #endif [Fact] public void Filter_Null() { @@ -146,10 +152,10 @@ public void Grouping() { Assert.Equal(4m, expandoResult[1].summary[0]); } - /* + #if NEWTONSOFT_TESTS [Fact] public void JArray() { - var sourceData = JsonSerializer.Deserialize(@"[ + var sourceData = JsonConvert.DeserializeObject(@"[ { ""p1"": 2 }, { ""p1"": 3 }, { ""p1"": 1 }, @@ -167,7 +173,7 @@ public void JArray() { Assert.Equal(4m, result.summary[0]); } - */ + #endif [Fact] public void T598818() { @@ -214,7 +220,7 @@ public void Issue227() { Assert.Single(loadResult.data); } - /* + #if NEWTONSOFT_TESTS [Fact] public void NoToStringForNumbers() { var compiler = new FilterExpressionCompiler(typeof(object), false); @@ -231,7 +237,7 @@ void Case(IList clientFilter, string expectedExpr, object trueTestValue) { 10 ); } - */ + #endif [Fact] public void T714342() { diff --git a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs index d1e7715c..0bf12fe3 100644 --- a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTests.cs @@ -143,7 +143,8 @@ public void Not() { [Fact] public void IsUnaryWithJsonCriteria() { - var crit = JsonSerializer.Deserialize("[\"!\", []]"); + var deserializedList = JsonSerializer.Deserialize("[\"!\", []]"); + var crit = Compatibility.UnwrapList(deserializedList); var compiler = new FilterExpressionCompiler(typeof(object), false); Assert.True(compiler.IsUnary(crit)); } @@ -238,15 +239,13 @@ public void T105740() { Assert.True((bool)Compile(new object[] { "Date", "12/13/2011 00:00:00" }).Compile().DynamicInvoke(data[0])); } - /* [Fact] public void JsonObjects() { - //TODO: - var crit = JsonSerializer.Deserialize(@"[ [ ""StringProp"", ""abc"" ], [ ""NullableProp"", null ] ]"); + var deserializedList = JsonSerializer.Deserialize(@"[ [ ""StringProp"", ""abc"" ], [ ""NullableProp"", null ] ]"); + var crit = Compatibility.UnwrapList(deserializedList); var expr = Compile(crit); Assert.Equal(@"((obj.StringProp == ""abc"") AndAlso (obj.NullableProp == null))", expr.Body.ToString()); } - */ [Fact] public void StringInequality() { diff --git a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs index 258ad286..37d1c80d 100644 --- a/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/FilterExpressionCompilerTypeConversionTests.cs @@ -1,8 +1,11 @@ using System; -using System.Collections; -using System.Text.Json; using Xunit; +#if NEWTONSOFT_TESTS +using System.Collections; +using Newtonsoft.Json; +#endif + namespace DevExtreme.AspNet.Data.Tests { public class FilterExpressionCompilerTypeConversionTests { @@ -222,17 +225,19 @@ public void StringFuncOnTimeSpan() { AssertEvaluation(obj, new[] { "NullableTime", "contains", "23" }); } - /* + #if NEWTONSOFT_TESTS [Theory] [InlineData(DateParseHandling.None)] [InlineData(DateParseHandling.DateTime)] [InlineData(DateParseHandling.DateTimeOffset)] public void Issue477(DateParseHandling dateParseHandling) { + //https://github.com/DevExpress/DevExtreme.AspNet.Data/pull/478 + //https://github.com/DevExpress/DevExtreme.AspNet.Data/issues/477 + var date = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero); - var filterJSON = JsonSerializer.Serialize(new object[] { "this", date }); - var deserializedFilter = JsonSerializer.Deserialize(filterJSON, new JsonSerializerOptions { - //TODO: - //DateParseHandling = dateParseHandling + var filterJSON = JsonConvert.SerializeObject(new object[] { "this", date }); + var deserializedFilter = JsonConvert.DeserializeObject(filterJSON, new JsonSerializerSettings { + DateParseHandling = dateParseHandling }); var loadOptions = new SampleLoadOptions { @@ -242,7 +247,7 @@ public void Issue477(DateParseHandling dateParseHandling) { var loadResult = DataSourceLoader.Load(new[] { date }, loadOptions); Assert.Single(loadResult.data); } - */ + #endif } } diff --git a/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs b/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs index 645465a0..8dbd5554 100644 --- a/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs +++ b/net/DevExtreme.AspNet.Data/Helpers/DataSourceLoadOptionsParser.cs @@ -63,10 +63,8 @@ public static void Parse(DataSourceLoadOptionsBase loadOptions, Func(group, DEFAULT_SERIALIZER_OPTIONS); if(!String.IsNullOrEmpty(filter)) { - loadOptions.Filter = JsonSerializer.Deserialize(filter, new JsonSerializerOptions { - //TODO: - //DateParseHandling = DateParseHandling.None - }); + var deserializedList = JsonSerializer.Deserialize(filter); + loadOptions.Filter = Compatibility.UnwrapList(deserializedList); } if(!String.IsNullOrEmpty(totalSummary)) diff --git a/net/DevExtreme.AspNet.Data/Utils.cs b/net/DevExtreme.AspNet.Data/Utils.cs index 8f423bf7..be402a57 100644 --- a/net/DevExtreme.AspNet.Data/Utils.cs +++ b/net/DevExtreme.AspNet.Data/Utils.cs @@ -1,9 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; +using System.Text.Json; namespace DevExtreme.AspNet.Data { @@ -116,10 +118,11 @@ public static int DynamicCompare(object selectorResult, object clientValue, bool } public static object UnwrapNewtonsoftValue(object value) { - /* - if(value is JValue jValue) - return jValue.Value; - */ + if(value != null) { + var type = value.GetType(); + if(type.FullName.Equals("Newtonsoft.Json.Linq.JValue")) + return type.GetProperty("Value").GetValue(value, null); + } return value; } @@ -136,4 +139,50 @@ static bool IsIntegralType(Type type) { } + internal static class Compatibility { + internal static IList UnwrapList(IList deserializedList) { + var unwrappedList = new List(); + foreach(var item in deserializedList) + unwrappedList.Add(UnwrapJsonElement(item)); + return unwrappedList; + } + + static object UnwrapJsonElement(object deserializeObject) { + if(!(deserializeObject is JsonElement jsonElement)) + return null; + + //https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to#deserialize-inferred-types-to-object-properties + + switch(jsonElement.ValueKind) { + case JsonValueKind.Array: + return jsonElement.EnumerateArray().Select(item => UnwrapJsonElement(item)).ToList(); + case JsonValueKind.String: + return jsonElement.GetString(); + case JsonValueKind.Null: + return null; + case JsonValueKind.False: + case JsonValueKind.True: + return jsonElement.GetBoolean(); + case JsonValueKind.Number: + //same as IsIntegralType + unsigned? + if(jsonElement.TryGetInt32(out var intValue)) + return intValue; + if(jsonElement.TryGetInt64(out var longValue)) + return longValue; + if(jsonElement.TryGetSByte(out var sByteValue)) + return sByteValue; + if(jsonElement.TryGetInt16(out var shortValue)) + return shortValue; + //or floating point as well? + if(jsonElement.TryGetDouble(out var doubleValue)) + return doubleValue; + if(jsonElement.TryGetDecimal(out var decimalValue)) + return decimalValue; + throw new NotImplementedException(); + default: + throw new NotImplementedException(); + } + } + } + } From 74d4f9b920fe7a76294ff5f1fb7eb0d32babf6ae Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Mon, 4 Mar 2024 19:36:23 +0200 Subject: [PATCH 10/15] Clean up --- net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs index cd01e0c1..929c2e50 100644 --- a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs @@ -6,11 +6,10 @@ namespace DevExtreme.AspNet.Data.Tests { public class ResponseModelTests { - [Fact] + [Fact(Skip = "Skip until consolidation or target bump to net7 and ShouldSerialize")] public void EmptyLoadResultSerialization() { //https://github.com/dotnet/runtime/issues/41630 //https://github.com/dotnet/runtime/issues/36236 - //TODO: Assert.Equal( "{\"data\":null}", JsonSerializer.Serialize(new LoadResult()) @@ -19,7 +18,6 @@ public void EmptyLoadResultSerialization() { [Fact] public void EmptyGroupSerialization() { - //TODO: var json = JsonSerializer.Serialize(new Group()); // these must always be present @@ -33,7 +31,6 @@ public void EmptyGroupSerialization() { #if NET4 [Fact] public void JavaScriptSerializer() { - //TODO: var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); var loadResultJson = serializer.Serialize(new LoadResult()); From e98cce67e7579d1b000a0b178bae436f2e05b4d9 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Mon, 4 Mar 2024 19:36:49 +0200 Subject: [PATCH 11/15] Revert "Add Newtonsoft src ref" This reverts commit 56b5cdf271d83a4fea63037647b76d94521d036b. --- net/DevExtreme.AspNet.Data.sln | 6 ------ net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj | 4 ---- 2 files changed, 10 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.sln b/net/DevExtreme.AspNet.Data.sln index b8c2806a..da73d43a 100644 --- a/net/DevExtreme.AspNet.Data.sln +++ b/net/DevExtreme.AspNet.Data.sln @@ -38,8 +38,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevExtreme.AspNet.Data.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevExtreme.AspNet.Data.Tests.EFCore8", "DevExtreme.AspNet.Data.Tests.EFCore8\DevExtreme.AspNet.Data.Tests.EFCore8.csproj", "{CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Newtonsoft.Json", "..\..\..\JamesNK\Newtonsoft.Json\Src\Newtonsoft.Json\Newtonsoft.Json.csproj", "{F5EB1902-0BA0-4930-A965-E6C947999CD6}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -102,10 +100,6 @@ Global {CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD8E0248-F0E8-4CE4-94C0-F8905E37D97F}.Release|Any CPU.Build.0 = Release|Any CPU - {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5EB1902-0BA0-4930-A965-E6C947999CD6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj index c209eafa..2727b64c 100644 --- a/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj +++ b/net/DevExtreme.AspNet.Data/DevExtreme.AspNet.Data.csproj @@ -32,10 +32,6 @@ - - - - From 7e1dea164b35b8d86c5224a5365f468d32a49938 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Mon, 4 Mar 2024 19:56:55 +0200 Subject: [PATCH 12/15] Recover Newtonsoft ref for explicit consumers only (non ef tests & sample) --- .../DevExtreme.AspNet.Data.Tests.NET4.csproj | 4 +++- .../DevExtreme.AspNet.Data.Tests.csproj | 1 + net/Sample/Controllers/NorthwindController.cs | 14 ++++++++------ net/Sample/Sample.csproj | 1 + 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj b/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj index aaaa43d7..e05e309e 100644 --- a/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj +++ b/net/DevExtreme.AspNet.Data.Tests.NET4/DevExtreme.AspNet.Data.Tests.NET4.csproj @@ -4,13 +4,15 @@ net472 DevExtreme.AspNet.Data.Tests DevExtreme.AspNet.Data.Tests - NET4 + NET4;NEWTONSOFT_TESTS + + diff --git a/net/DevExtreme.AspNet.Data.Tests/DevExtreme.AspNet.Data.Tests.csproj b/net/DevExtreme.AspNet.Data.Tests/DevExtreme.AspNet.Data.Tests.csproj index 73074112..df27a61b 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DevExtreme.AspNet.Data.Tests.csproj +++ b/net/DevExtreme.AspNet.Data.Tests/DevExtreme.AspNet.Data.Tests.csproj @@ -2,6 +2,7 @@ net6.0 + NEWTONSOFT_TESTS diff --git a/net/Sample/Controllers/NorthwindController.cs b/net/Sample/Controllers/NorthwindController.cs index 1b86ed1d..078968a5 100644 --- a/net/Sample/Controllers/NorthwindController.cs +++ b/net/Sample/Controllers/NorthwindController.cs @@ -1,10 +1,14 @@ using DevExtreme.AspNet.Data; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; + using Sample.Models; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +using Newtonsoft.Json; + namespace Sample.Controllers { [Route("nwind")] @@ -77,8 +81,7 @@ public async Task UpdateOrder(int key, string values) { if(order == null) return StatusCode(409, "Order not found"); - //TODO: - //JsonConvert.PopulateObject(values, order); + JsonConvert.PopulateObject(values, order); if(!TryValidateModel(order)) return BadRequest(ModelState.ToFullErrorString()); @@ -92,8 +95,7 @@ public async Task UpdateOrder(int key, string values) { public async Task InsertOrder(string values) { var order = new Order(); - //TODO: - //JsonConvert.PopulateObject(values, order); + JsonConvert.PopulateObject(values, order); if(!TryValidateModel(order)) return BadRequest(ModelState.ToFullErrorString()); diff --git a/net/Sample/Sample.csproj b/net/Sample/Sample.csproj index e9168b07..6cc8e548 100644 --- a/net/Sample/Sample.csproj +++ b/net/Sample/Sample.csproj @@ -8,6 +8,7 @@ + From 094a3b6afec79256cb89c2154ebb35a5a7ebb2b7 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Tue, 5 Mar 2024 15:20:48 +0200 Subject: [PATCH 13/15] LoadResult: discard counter part Ignore attrs until proper support ShouldSerialize (Blazor Grid3 may fails without totalCount) --- .../ResponseModelExTests.cs | 33 +++++++++++++++++++ .../ResponseModelTests.cs | 3 +- .../ResponseModel/LoadResult.cs | 4 +-- 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs diff --git a/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs b/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs new file mode 100644 index 00000000..08093925 --- /dev/null +++ b/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs @@ -0,0 +1,33 @@ +#if NEWTONSOFT_TESTS +using DevExtreme.AspNet.Data.ResponseModel; + +using System.ComponentModel; +using Xunit; + +using Newtonsoft.Json; + +namespace DevExtreme.AspNet.Data.Tests { + + public class ResponseModelTestsEx { + + class LoadResultEx : LoadResult { + [DefaultValue(-1), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public new int totalCount { get; set; } = -1; + [DefaultValue(-1), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public new int groupCount { get; set; } = -1; + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public new object[] summary { get; set; } + } + + [Fact] + public void EmptyLoadResultSerialization() { + Assert.Equal( + "{\"data\":null}", + JsonConvert.SerializeObject(new LoadResultEx()) + ); + } + + } + +} +#endif diff --git a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs index 929c2e50..aaf26dca 100644 --- a/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/ResponseModelTests.cs @@ -1,4 +1,5 @@ using DevExtreme.AspNet.Data.ResponseModel; + using System.Text.Json; using Xunit; @@ -11,7 +12,7 @@ public void EmptyLoadResultSerialization() { //https://github.com/dotnet/runtime/issues/41630 //https://github.com/dotnet/runtime/issues/36236 Assert.Equal( - "{\"data\":null}", + "{\"data\":null,\"totalCount\":-1,\"groupCount\":-1}", JsonSerializer.Serialize(new LoadResult()) ); } diff --git a/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs b/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs index 38ad17f4..f59e79f2 100644 --- a/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs +++ b/net/DevExtreme.AspNet.Data/ResponseModel/LoadResult.cs @@ -16,13 +16,13 @@ public class LoadResult { /// /// The total number of data objects in the resulting dataset. /// - [DefaultValue(-1), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DefaultValue(-1)/*, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)*/] public int totalCount { get; set; } = -1; /// /// The number of top-level groups in the resulting dataset. /// - [DefaultValue(-1), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DefaultValue(-1)/*, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)*/] public int groupCount { get; set; } = -1; /// From 38f382bf919faf53372a07d25139ec4706a74b81 Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Tue, 5 Mar 2024 16:34:59 +0200 Subject: [PATCH 14/15] NumericAndStringConverter for GroupingInfo -> GroupInterval --- .../DataSourceLoadOptionsParserTests.cs | 15 ++++ .../ResponseModelExTests.cs | 2 +- net/DevExtreme.AspNet.Data/Compatibility.cs | 69 +++++++++++++++++++ net/DevExtreme.AspNet.Data/GroupingInfo.cs | 6 +- net/DevExtreme.AspNet.Data/Utils.cs | 48 ------------- 5 files changed, 87 insertions(+), 53 deletions(-) create mode 100644 net/DevExtreme.AspNet.Data/Compatibility.cs diff --git a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs index 7b70fa8c..10064cdb 100644 --- a/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/DataSourceLoadOptionsParserTests.cs @@ -59,6 +59,21 @@ public void MustNotParseDates() { Assert.IsType(opts.Filter[1]); } + [Fact] + public void MustParseNumericAsString() { + var opts = new SampleLoadOptions(); + + DataSourceLoadOptionsParser.Parse(opts, key => { + if(key == DataSourceLoadOptionsParser.KEY_GROUP) + return @"[{""selector"":""freight"",""groupInterval"":100,""isExpanded"":false}]"; + return null; + }); + + Assert.Equal("freight", opts.Group[0].Selector); + Assert.Equal("100", opts.Group[0].GroupInterval); + Assert.False(opts.Group[0].IsExpanded); + } + } } diff --git a/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs b/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs index 08093925..f5eaa134 100644 --- a/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs +++ b/net/DevExtreme.AspNet.Data.Tests/ResponseModelExTests.cs @@ -7,7 +7,7 @@ using Newtonsoft.Json; namespace DevExtreme.AspNet.Data.Tests { - + public class ResponseModelTestsEx { class LoadResultEx : LoadResult { diff --git a/net/DevExtreme.AspNet.Data/Compatibility.cs b/net/DevExtreme.AspNet.Data/Compatibility.cs new file mode 100644 index 00000000..daa29d9b --- /dev/null +++ b/net/DevExtreme.AspNet.Data/Compatibility.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace DevExtreme.AspNet.Data { + + static class Compatibility { + public static IList UnwrapList(IList deserializedList) { + var unwrappedList = new List(); + foreach(var item in deserializedList) + unwrappedList.Add(UnwrapJsonElement(item)); + return unwrappedList; + } + + static object UnwrapJsonElement(object deserializeObject) { + if(!(deserializeObject is JsonElement jsonElement)) + return null; + + switch(jsonElement.ValueKind) { + case JsonValueKind.Array: + return jsonElement.EnumerateArray().Select(item => UnwrapJsonElement(item)).ToList(); + case JsonValueKind.String: + return jsonElement.GetString(); + case JsonValueKind.Null: + return null; + case JsonValueKind.False: + case JsonValueKind.True: + return jsonElement.GetBoolean(); + case JsonValueKind.Number: + //same as IsIntegralType + unsigned? + if(jsonElement.TryGetInt32(out var intValue)) + return intValue; + //or floating point as well? + //we primarily use Convert.ToDecimal everywhere + if(jsonElement.TryGetDecimal(out var decimalValue)) + return decimalValue; + throw new NotImplementedException(); + default: + throw new NotImplementedException(); + } + } + } + + class NumericAndStringConverter : JsonConverter { + public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if(reader.TokenType == JsonTokenType.Number) + return string.Format(CultureInfo.InvariantCulture, "{0}", GetNumber(ref reader)); + return reader.GetString(); + } + static object GetNumber(ref Utf8JsonReader reader) { + if(reader.TryGetInt32(out int intValue)) + return intValue; + if(reader.TryGetDecimal(out decimal decimalValue)) + return decimalValue; + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) { + throw new NotImplementedException(); + } + + } + +} diff --git a/net/DevExtreme.AspNet.Data/GroupingInfo.cs b/net/DevExtreme.AspNet.Data/GroupingInfo.cs index 14edd43f..7161d067 100644 --- a/net/DevExtreme.AspNet.Data/GroupingInfo.cs +++ b/net/DevExtreme.AspNet.Data/GroupingInfo.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Text.Json.Serialization; namespace DevExtreme.AspNet.Data { @@ -12,6 +9,7 @@ public class GroupingInfo : SortingInfo { /// /// A value that groups data in ranges of a given length or date/time period. /// + [JsonConverter(typeof(NumericAndStringConverter))] public string GroupInterval { get; set; } /// diff --git a/net/DevExtreme.AspNet.Data/Utils.cs b/net/DevExtreme.AspNet.Data/Utils.cs index be402a57..c24c70d0 100644 --- a/net/DevExtreme.AspNet.Data/Utils.cs +++ b/net/DevExtreme.AspNet.Data/Utils.cs @@ -1,11 +1,9 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; -using System.Text.Json; namespace DevExtreme.AspNet.Data { @@ -139,50 +137,4 @@ static bool IsIntegralType(Type type) { } - internal static class Compatibility { - internal static IList UnwrapList(IList deserializedList) { - var unwrappedList = new List(); - foreach(var item in deserializedList) - unwrappedList.Add(UnwrapJsonElement(item)); - return unwrappedList; - } - - static object UnwrapJsonElement(object deserializeObject) { - if(!(deserializeObject is JsonElement jsonElement)) - return null; - - //https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to#deserialize-inferred-types-to-object-properties - - switch(jsonElement.ValueKind) { - case JsonValueKind.Array: - return jsonElement.EnumerateArray().Select(item => UnwrapJsonElement(item)).ToList(); - case JsonValueKind.String: - return jsonElement.GetString(); - case JsonValueKind.Null: - return null; - case JsonValueKind.False: - case JsonValueKind.True: - return jsonElement.GetBoolean(); - case JsonValueKind.Number: - //same as IsIntegralType + unsigned? - if(jsonElement.TryGetInt32(out var intValue)) - return intValue; - if(jsonElement.TryGetInt64(out var longValue)) - return longValue; - if(jsonElement.TryGetSByte(out var sByteValue)) - return sByteValue; - if(jsonElement.TryGetInt16(out var shortValue)) - return shortValue; - //or floating point as well? - if(jsonElement.TryGetDouble(out var doubleValue)) - return doubleValue; - if(jsonElement.TryGetDecimal(out var decimalValue)) - return decimalValue; - throw new NotImplementedException(); - default: - throw new NotImplementedException(); - } - } - } - } From 4b8631a9864cc6a5cfb79c4d19dcc8d73c1a9d2a Mon Sep 17 00:00:00 2001 From: Mikhail Preyskurantov Date: Thu, 7 Mar 2024 16:51:30 +0200 Subject: [PATCH 15/15] Review / Suggestion: thrown exception instead of null for non JsonElement --- net/DevExtreme.AspNet.Data/Compatibility.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/DevExtreme.AspNet.Data/Compatibility.cs b/net/DevExtreme.AspNet.Data/Compatibility.cs index daa29d9b..0040b3d5 100644 --- a/net/DevExtreme.AspNet.Data/Compatibility.cs +++ b/net/DevExtreme.AspNet.Data/Compatibility.cs @@ -19,7 +19,7 @@ public static IList UnwrapList(IList deserializedList) { static object UnwrapJsonElement(object deserializeObject) { if(!(deserializeObject is JsonElement jsonElement)) - return null; + throw new InvalidOperationException(); switch(jsonElement.ValueKind) { case JsonValueKind.Array: @@ -52,6 +52,7 @@ public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS return string.Format(CultureInfo.InvariantCulture, "{0}", GetNumber(ref reader)); return reader.GetString(); } + static object GetNumber(ref Utf8JsonReader reader) { if(reader.TryGetInt32(out int intValue)) return intValue;