diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 7ca9d6e3..8aefa397 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 JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi if (source.Count == 0) { - return new JArray(); + return []; } var queryable = ToQueryable(source, config); @@ -848,7 +848,8 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi private static JArray ToJArray(Func func) { var array = new JArray(); - foreach (var dynamicElement in func()) + var funcResult = func(); + foreach (var dynamicElement in funcResult) { var element = dynamicElement switch { diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs index 7d72ad61..fefd5b66 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs @@ -361,7 +361,12 @@ public bool ExpressionQualifiesForNullPropagation(Expression? expression) public Expression GenerateDefaultExpression(Type type) { #if NET35 - return Expression.Constant(Activator.CreateInstance(type)); + if (type.IsValueType) + { + return Expression.Constant(Activator.CreateInstance(type), type); + } + + return Expression.Constant(null, type); #else return Expression.Default(type); #endif @@ -388,11 +393,19 @@ public bool TryConvertTypes(ref Expression left, ref Expression right) if (left.Type == typeof(object)) { - left = Expression.Convert(left, right.Type); + left = Expression.Condition( + Expression.Equal(left, Expression.Constant(null, typeof(object))), + GenerateDefaultExpression(right.Type), + Expression.Convert(left, right.Type) + ); } else if (right.Type == typeof(object)) { - right = Expression.Convert(right, left.Type); + right = Expression.Condition( + Expression.Equal(right, Expression.Constant(null, typeof(object))), + GenerateDefaultExpression(left.Type), + Expression.Convert(right, left.Type) + ); } return true; diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs index bbc691cd..4e52949b 100644 --- a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs @@ -52,5 +52,5 @@ internal interface IExpressionHelper /// /// If the types are different (and not null), try to convert the object type to other type. /// - public bool TryConvertTypes(ref Expression left, ref Expression right); + bool TryConvertTypes(ref Expression left, ref Expression right); } \ 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 2e94a212..40185d45 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -1,4 +1,5 @@ -using FluentAssertions; +using System.Linq.Dynamic.Core.NewtonsoftJson.Config; +using FluentAssertions; using Newtonsoft.Json.Linq; using Xunit; @@ -506,4 +507,62 @@ public void Where_With_Select() var first = result.First(); first.Value().Should().Be("Doe"); } + + //[Fact] + //public void Where_OptionalProperty() + //{ + // // Arrange + // var config = new NewtonsoftJsonParsingConfig + // { + // ConvertObjectToSupportComparison = true + // }; + // var array = + // """ + // [ + // { + // "Name": "John", + // "Age": 30 + // }, + // { + // "Name": "Doe" + // } + // ] + // """; + + // // Act + // var result = JArray.Parse(array).Where(config, "Age > 30").Select("Name"); + + // // Assert + // result.Should().HaveCount(1); + // var first = result.First(); + // first.Value().Should().Be("John"); + //} + + [Theory] + [InlineData("notExisting == true")] + [InlineData("notExisting == \"true\"")] + [InlineData("notExisting == 1")] + [InlineData("notExisting == \"1\"")] + [InlineData("notExisting == \"something\"")] + [InlineData("notExisting > 1")] + [InlineData("true == notExisting")] + [InlineData("\"true\" == notExisting")] + [InlineData("1 == notExisting")] + [InlineData("\"1\" == notExisting")] + [InlineData("\"something\" == notExisting")] + [InlineData("1 < notExisting")] + public void Where_NonExistingMember_EmptyResult(string predicate) + { + // Arrange + var config = new NewtonsoftJsonParsingConfig + { + ConvertObjectToSupportComparison = true + }; + + // Act + var result = _source.Where(config, predicate); + + // Assert + result.Should().BeEmpty(); + } } \ 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 f5dee221..1e817664 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.Linq.Dynamic.Core.SystemTextJson.Config; +using System.Text.Json; using FluentAssertions; using Xunit; @@ -535,4 +536,32 @@ public void Where_With_Select() array.Should().HaveCount(1); array.First().GetString().Should().Be("Doe"); } + + [Theory] + [InlineData("notExisting == true")] + [InlineData("notExisting == \"true\"")] + [InlineData("notExisting == 1")] + [InlineData("notExisting == \"1\"")] + [InlineData("notExisting == \"something\"")] + [InlineData("notExisting > 1")] + [InlineData("true == notExisting")] + [InlineData("\"true\" == notExisting")] + [InlineData("1 == notExisting")] + [InlineData("\"1\" == notExisting")] + [InlineData("\"something\" == notExisting")] + [InlineData("1 < notExisting")] + public void Where_NonExistingMember_EmptyResult(string predicate) + { + // Arrange + var config = new SystemTextJsonParsingConfig + { + ConvertObjectToSupportComparison = true + }; + + // Act + var result = _source.Where(config, predicate).RootElement.EnumerateArray(); + + // Assert + result.Should().BeEmpty(); + } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs index 32dd3002..6d33cae8 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs @@ -131,20 +131,24 @@ public void DynamicClass_GettingValue_ByIndex_Should_Work() public void DynamicClass_SettingExistingPropertyValue_ByIndex_Should_Work() { // Arrange - var test = "Test"; - var newTest = "abc"; - var range = new List + var originalValue = "Test"; + var newValue = "abc"; + var array = new object[] { - new { FieldName = test, Value = 3.14159 } + new + { + FieldName = originalValue, + Value = 3.14159 + } }; // Act - var rangeResult = range.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList(); + var rangeResult = array.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList(); var item = rangeResult.First(); - item["FieldName"] = newTest; + item["FieldName"] = newValue; var value = item["FieldName"] as string; - value.Should().Be(newTest); + value.Should().Be(newValue); } [Fact]