diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs
index fbccbf64..3cb24306 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs
@@ -10,7 +10,10 @@ public class NewtonsoftJsonParsingConfig : ParsingConfig
///
/// The default ParsingConfig for .
///
- public new static NewtonsoftJsonParsingConfig Default { get; } = new();
+ public new static NewtonsoftJsonParsingConfig Default { get; } = new NewtonsoftJsonParsingConfig
+ {
+ ConvertObjectToSupportComparison = true
+ };
///
/// The default to use.
@@ -28,7 +31,7 @@ public class NewtonsoftJsonParsingConfig : ParsingConfig
///
/// Use this property to control how the normalization process handles properties that are missing or undefined.
/// The selected behavior may affect the output or error handling of normalization operations.
- /// The default value is .
+ /// The default value is .
///
public NormalizationNonExistingPropertyBehavior NormalizationNonExistingPropertyValueBehavior { get; set; }
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs
index bb68277b..44608e83 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs
@@ -6,12 +6,12 @@
public enum NormalizationNonExistingPropertyBehavior
{
///
- /// Specifies that the default value should be used.
+ /// Specifies that a null value should be used.
///
- UseDefaultValue = 0,
+ UseNull = 0,
///
- /// Specifies that null values should be used.
+ /// Specifies that the default value should be used.
///
- UseNull = 1
+ UseDefaultValue = 1
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs
index 1a7be2f4..2f79840b 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs
@@ -44,10 +44,7 @@ private class JTokenResolvers : Dictionary func)
private static IQueryable ToQueryable(JArray source, NewtonsoftJsonParsingConfig? config = null)
{
- var normalized = config?.Normalize == true ?
- NormalizeUtils.NormalizeArray(source, config.NormalizationNonExistingPropertyValueBehavior):
+ config = config ?? NewtonsoftJsonParsingConfig.Default;
+ config.ConvertObjectToSupportComparison = true;
+
+ var normalized = config.Normalize == true ?
+ NormalizeUtils.NormalizeArray(source, config.NormalizationNonExistingPropertyValueBehavior) :
source;
return normalized
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs
index 5669915c..fe980319 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs
@@ -86,7 +86,7 @@ private static JObject NormalizeObject(JObject source, Dictionary schem
}
else
{
- obj[key] = normalizationBehavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(schema[key]) : JValue.CreateNull();
+ obj[key] = GetDefaultOrNullValue(normalizationBehavior, schema[key]);
}
}
@@ -125,7 +125,28 @@ private static JToken GetDefaultValue(JsonValueInfo jType)
JTokenType.Integer => default(int),
JTokenType.String => string.Empty,
JTokenType.TimeSpan => TimeSpan.MinValue,
+ _ => GetNullValue(jType),
+ };
+ }
+
+ private static JValue GetNullValue(JsonValueInfo jType)
+ {
+ return jType.Type switch
+ {
+ JTokenType.Boolean => new JValue((bool?)null),
+ JTokenType.Bytes => new JValue((byte[]?)null),
+ JTokenType.Date => new JValue((DateTime?)null),
+ JTokenType.Float => new JValue((float?)null),
+ JTokenType.Guid => new JValue((Guid?)null),
+ JTokenType.Integer => new JValue((int?)null),
+ JTokenType.String => new JValue((string?)null),
+ JTokenType.TimeSpan => new JValue((TimeSpan?)null),
_ => JValue.CreateNull(),
};
}
+
+ private static JToken GetDefaultOrNullValue(NormalizationNonExistingPropertyBehavior behavior, JsonValueInfo jType)
+ {
+ return behavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : GetNullValue(jType);
+ }
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs
index daafaa4b..381f0408 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs
@@ -1,17 +1,17 @@
namespace System.Linq.Dynamic.Core.SystemTextJson.Config;
///
-/// Specifies the behavior to use when setting a property vlue that does not exist or is missing during normalization.
+/// Specifies the behavior to use when setting a property value that does not exist or is missing during normalization.
///
public enum NormalizationNonExistingPropertyBehavior
{
///
- /// Specifies that the default value should be used.
+ /// Specifies that a null value should be used.
///
- UseDefaultValue = 0,
+ UseNull = 0,
///
- /// Specifies that null values should be used.
+ /// Specifies that the default value should be used.
///
- UseNull = 1
+ UseDefaultValue = 1
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs
index a4c1e76a..4892bcf2 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs
@@ -24,7 +24,7 @@ public class SystemTextJsonParsingConfig : ParsingConfig
///
/// Use this property to control how the normalization process handles properties that are missing or undefined.
/// The selected behavior may affect the output or error handling of normalization operations.
- /// The default value is .
+ /// The default value is .
///
public NormalizationNonExistingPropertyBehavior NormalizationNonExistingPropertyValueBehavior { 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
index 7daf15a5..3437d3c9 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs
@@ -34,10 +34,7 @@ private class JTokenResolvers : Dictionary src, Type newType)
{
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
- return (IEnumerable)method.Invoke(null, new object[] { src })!;
+ return (IEnumerable)method.Invoke(null, [src])!;
}
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs
index fa5836d2..7f6e4abf 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs
@@ -104,15 +104,16 @@ private static JsonObject NormalizeObject(JsonObject source, Dictionary sc
}
else
{
- obj[key] = normalizationBehavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : null;
+ obj[key] = GetDefaultOrNullValue(normalizationBehavior, jType);
}
}
@@ -150,7 +151,25 @@ private static JsonObject CreateEmptyObject(Dictionary sc
JsonValueKind.Number => default(int),
JsonValueKind.String => string.Empty,
JsonValueKind.True => false,
+ _ => GetNullValue(jType),
+ };
+ }
+
+ private static JsonNode? GetNullValue(JsonValueInfo jType)
+ {
+ return jType.Type switch
+ {
+ JsonValueKind.Array => null,
+ JsonValueKind.False => JsonValue.Create(false),
+ JsonValueKind.Number => JsonValue.Create(null),
+ JsonValueKind.String => JsonValue.Create(null),
+ JsonValueKind.True => JsonValue.Create(true),
_ => null,
};
}
+
+ private static JsonNode? GetDefaultOrNullValue(NormalizationNonExistingPropertyBehavior behavior, JsonValueInfo jType)
+ {
+ return behavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : GetNullValue(jType);
+ }
}
\ 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 e391ed29..d07d8052 100644
--- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
+++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
@@ -1,5 +1,4 @@
-using System.Linq.Dynamic.Core.Exceptions;
-using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
+using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Xunit;
@@ -13,11 +12,13 @@ public class NewtonsoftJsonTests
[
{
"Name": "John",
- "Age": 30
+ "Age": 30,
+ "IsNull": null
},
{
"Name": "Doe",
- "Age": 40
+ "Age": 40,
+ "AlsoNull": null
}
]
""";
@@ -567,6 +568,6 @@ public void NormalizeArray_When_NormalizeIsFalse_ShouldThrow()
Action act = () => JArray.Parse(array).Where(config, "Age >= 30");
// Assert
- act.Should().Throw().WithMessage("The binary operator GreaterThanOrEqual is not defined for the types 'System.Object' and 'System.Int32'.");
+ act.Should().Throw().WithMessage("Unable to find property 'Age' on type '<>*");
}
}
\ 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 e699e086..dd5e62ee 100644
--- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
+++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
@@ -17,11 +17,13 @@ public class SystemTextJsonTests
[
{
"Name": "John",
- "Age": 30
+ "Age": 30,
+ "IsNull": null
},
{
"Name": "Doe",
- "Age": 40
+ "Age": 40,
+ "AlsoNull": null
}
]
""";
@@ -161,20 +163,20 @@ public void Distinct()
public void First()
{
// Act + Assert 1
- _source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText());
+ _source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}
[Fact]
public void FirstOrDefault()
{
// Act + Assert 1
- _source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText());
+ _source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 3
_source.FirstOrDefault("Age > 999").Should().BeNull();
@@ -267,20 +269,20 @@ public void GroupBySimpleKeySelector()
public void Last()
{
// Act + Assert 1
- _source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}
[Fact]
public void LastOrDefault()
{
// Act + Assert 1
- _source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 3
_source.LastOrDefault("Age > 999").Should().BeNull();
@@ -444,7 +446,7 @@ public void SelectMany()
public void Single()
{
// Act + Assert
- _source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}
[Fact]