Skip to content

Commit 3030208

Browse files
committed
Fix ExpressionHelper.TryConvertTypes to generate correct Convert in case left or right is null
1 parent 9f59211 commit 3030208

File tree

6 files changed

+136
-14
lines changed

6 files changed

+136
-14
lines changed

src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi
821821

822822
if (source.Count == 0)
823823
{
824-
return new JArray();
824+
return [];
825825
}
826826

827827
var queryable = ToQueryable(source, config);
@@ -848,7 +848,8 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi
848848
private static JArray ToJArray(Func<IQueryable> func)
849849
{
850850
var array = new JArray();
851-
foreach (var dynamicElement in func())
851+
var funcResult = func();
852+
foreach (var dynamicElement in funcResult)
852853
{
853854
var element = dynamicElement switch
854855
{

src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,16 +388,40 @@ public bool TryConvertTypes(ref Expression left, ref Expression right)
388388

389389
if (left.Type == typeof(object))
390390
{
391-
left = Expression.Convert(left, right.Type);
391+
left = Expression.Condition(
392+
Expression.Equal(left, Expression.Constant(null, typeof(object))),
393+
GenerateDefault(right.Type),
394+
Expression.Convert(left, right.Type)
395+
);
392396
}
393397
else if (right.Type == typeof(object))
394398
{
395-
right = Expression.Convert(right, left.Type);
399+
right = Expression.Condition(
400+
Expression.Equal(right, Expression.Constant(null, typeof(object))),
401+
GenerateDefault(left.Type),
402+
Expression.Convert(right, left.Type)
403+
);
396404
}
397405

398406
return true;
399407
}
400408

409+
public Expression GenerateDefault(Type type)
410+
{
411+
#if NET35
412+
// .NET 3.5 doesn't have Expression.Default
413+
if (type.IsValueType)
414+
{
415+
return Expression.Constant(Activator.CreateInstance(type), type);
416+
}
417+
418+
return Expression.Constant(null, type);
419+
#else
420+
return Expression.Default(type);
421+
#endif
422+
}
423+
424+
401425
private Expression? GetMemberExpression(Expression? expression)
402426
{
403427
if (ExpressionQualifiesForNullPropagation(expression))

src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,7 @@ internal interface IExpressionHelper
5252
/// <summary>
5353
/// If the types are different (and not null), try to convert the object type to other type.
5454
/// </summary>
55-
public bool TryConvertTypes(ref Expression left, ref Expression right);
55+
bool TryConvertTypes(ref Expression left, ref Expression right);
56+
57+
Expression GenerateDefault(Type type);
5658
}

test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using FluentAssertions;
1+
using System.Collections.Generic;
2+
using System.Linq.Dynamic.Core.Exceptions;
3+
using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
4+
using FluentAssertions;
25
using Newtonsoft.Json.Linq;
36
using Xunit;
47

@@ -506,4 +509,62 @@ public void Where_With_Select()
506509
var first = result.First();
507510
first.Value<string>().Should().Be("Doe");
508511
}
512+
513+
//[Fact]
514+
//public void Where_OptionalProperty()
515+
//{
516+
// // Arrange
517+
// var config = new NewtonsoftJsonParsingConfig
518+
// {
519+
// ConvertObjectToSupportComparison = true
520+
// };
521+
// var array =
522+
// """
523+
// [
524+
// {
525+
// "Name": "John",
526+
// "Age": 30
527+
// },
528+
// {
529+
// "Name": "Doe"
530+
// }
531+
// ]
532+
// """;
533+
534+
// // Act
535+
// var result = JArray.Parse(array).Where(config, "Age > 30").Select("Name");
536+
537+
// // Assert
538+
// result.Should().HaveCount(1);
539+
// var first = result.First();
540+
// first.Value<string>().Should().Be("John");
541+
//}
542+
543+
[Theory]
544+
[InlineData("notExisting == true")]
545+
[InlineData("notExisting == \"true\"")]
546+
[InlineData("notExisting == 1")]
547+
[InlineData("notExisting == \"1\"")]
548+
[InlineData("notExisting == \"something\"")]
549+
[InlineData("notExisting > 1")]
550+
[InlineData("true == notExisting")]
551+
[InlineData("\"true\" == notExisting")]
552+
[InlineData("1 == notExisting")]
553+
[InlineData("\"1\" == notExisting")]
554+
[InlineData("\"something\" == notExisting")]
555+
[InlineData("1 < notExisting")]
556+
public void Where_NonExistingMember_EmptyResult(string predicate)
557+
{
558+
// Arrange
559+
var config = new NewtonsoftJsonParsingConfig
560+
{
561+
ConvertObjectToSupportComparison = true
562+
};
563+
564+
// Act
565+
var result = _source.Where(config, predicate);
566+
567+
// Assert
568+
result.Should().BeEmpty();
569+
}
509570
}

test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Text.Json;
1+
using System.Linq.Dynamic.Core.Exceptions;
2+
using System.Linq.Dynamic.Core.SystemTextJson.Config;
3+
using System.Text.Json;
24
using FluentAssertions;
35
using Xunit;
46

@@ -535,4 +537,32 @@ public void Where_With_Select()
535537
array.Should().HaveCount(1);
536538
array.First().GetString().Should().Be("Doe");
537539
}
540+
541+
[Theory]
542+
[InlineData("notExisting == true")]
543+
[InlineData("notExisting == \"true\"")]
544+
[InlineData("notExisting == 1")]
545+
[InlineData("notExisting == \"1\"")]
546+
[InlineData("notExisting == \"something\"")]
547+
[InlineData("notExisting > 1")]
548+
[InlineData("true == notExisting")]
549+
[InlineData("\"true\" == notExisting")]
550+
[InlineData("1 == notExisting")]
551+
[InlineData("\"1\" == notExisting")]
552+
[InlineData("\"something\" == notExisting")]
553+
[InlineData("1 < notExisting")]
554+
public void Where_NonExistingMember_EmptyResult(string predicate)
555+
{
556+
// Arrange
557+
var config = new SystemTextJsonParsingConfig
558+
{
559+
ConvertObjectToSupportComparison = true
560+
};
561+
562+
// Act
563+
var result = _source.Where(config, predicate).RootElement.EnumerateArray();
564+
565+
// Assert
566+
result.Should().BeEmpty();
567+
}
538568
}

test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,24 @@ public void DynamicClass_GettingValue_ByIndex_Should_Work()
131131
public void DynamicClass_SettingExistingPropertyValue_ByIndex_Should_Work()
132132
{
133133
// Arrange
134-
var test = "Test";
135-
var newTest = "abc";
136-
var range = new List<object>
134+
var originalValue = "Test";
135+
var newValue = "abc";
136+
var array = new object[]
137137
{
138-
new { FieldName = test, Value = 3.14159 }
138+
new
139+
{
140+
FieldName = originalValue,
141+
Value = 3.14159
142+
}
139143
};
140144

141145
// Act
142-
var rangeResult = range.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList();
146+
var rangeResult = array.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList();
143147
var item = rangeResult.First();
144148

145-
item["FieldName"] = newTest;
149+
item["FieldName"] = newValue;
146150
var value = item["FieldName"] as string;
147-
value.Should().Be(newTest);
151+
value.Should().Be(newValue);
148152
}
149153

150154
[Fact]

0 commit comments

Comments
 (0)