diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f7ce7ac..4a4ae9bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v1.7.1 (29 November 2025) +- [#961](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/961) - Fix Json when property value is null [bug] contributed by [StefH](https://github.com/StefH) +- [#962](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/962) - json: fix logic when property is not found [bug] contributed by [StefH](https://github.com/StefH) +- [#965](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/965) - Fix NumberParser for integer < int.MinValue [bug] contributed by [StefH](https://github.com/StefH) +- [#960](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/960) - json: follow up for not existing members [bug] +- [#964](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/964) - Integer numbers smaller than int.MinValue are not parsed correctly [bug] + # v1.7.0 (15 November 2025) - [#956](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/956) - Fix parsing Hex and Binary [bug] contributed by [StefH](https://github.com/StefH) - [#957](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/957) - .NET 10 [feature] contributed by [StefH](https://github.com/StefH) diff --git a/Generate-ReleaseNotes.bat b/Generate-ReleaseNotes.bat index 07a52300..809d22d8 100644 --- a/Generate-ReleaseNotes.bat +++ b/Generate-ReleaseNotes.bat @@ -1,5 +1,5 @@ rem https://github.com/StefH/GitHubReleaseNotes -SET version=v1.7.0 +SET version=v1.7.1 GitHubReleaseNotes --output CHANGELOG.md --exclude-labels known_issue out_of_scope not_planned invalid question documentation wontfix environment duplicate --language en --version %version% --token %GH_TOKEN% 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/src/System.Linq.Dynamic.Core/DynamicClass.cs b/src/System.Linq.Dynamic.Core/DynamicClass.cs index 33f06aee..64685f5e 100644 --- a/src/System.Linq.Dynamic.Core/DynamicClass.cs +++ b/src/System.Linq.Dynamic.Core/DynamicClass.cs @@ -123,6 +123,16 @@ public object? this[string name] } } + /// + /// Determines whether a property with the specified name exists in the collection. + /// + /// The name of the property to locate. Cannot be null. + /// true if a property with the specified name exists; otherwise, false. + public bool ContainsProperty(string name) + { + return Properties.ContainsKey(name); + } + /// /// Returns the enumeration of all dynamic member names. /// diff --git a/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs b/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs index 8778de98..cf28afd3 100644 --- a/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs +++ b/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs @@ -12,7 +12,7 @@ public class DynamicClass : DynamicObject { internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer"; - private readonly Dictionary _properties = new(); + private readonly Dictionary _properties = new(); /// /// Initializes a new instance of the class. @@ -35,7 +35,7 @@ public DynamicClass(params KeyValuePair[] propertylist) /// The name. /// Value from the property. [IndexerName(IndexerName)] - public object this[string name] + public object? this[string name] { get { @@ -59,6 +59,16 @@ public object this[string name] } } + /// + /// Determines whether a property with the specified name exists in the collection. + /// + /// The name of the property to locate. Cannot be null. + /// true if a property with the specified name exists; otherwise, false. + public bool ContainsProperty(string name) + { + return _properties.ContainsKey(name); + } + /// /// Returns the enumeration of all dynamic member names. /// diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs index fefd5b66..8222d4d7 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs @@ -11,6 +11,7 @@ namespace System.Linq.Dynamic.Core.Parser; internal class ExpressionHelper : IExpressionHelper { + private static readonly Expression _nullExpression = Expression.Constant(null); private readonly IConstantExpressionWrapper _constantExpressionWrapper = new ConstantExpressionWrapper(); private readonly ParsingConfig _parsingConfig; @@ -340,7 +341,7 @@ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, boo // Convert all expressions into '!= null' expressions (only if the type can be null) var binaryExpressions = expressions .Where(expression => TypeHelper.TypeCanBeNull(expression.Type)) - .Select(expression => Expression.NotEqual(expression, Expression.Constant(null))) + .Select(expression => Expression.NotEqual(expression, _nullExpression)) .ToArray(); // Convert all binary expressions into `AndAlso(...)` @@ -393,16 +394,46 @@ public bool TryConvertTypes(ref Expression left, ref Expression right) if (left.Type == typeof(object)) { + if (TryGetAsIndexerExpression(left, out var ce)) + { + var rightTypeAsNullableType = TypeHelper.GetNullableType(right.Type); + + right = Expression.Convert(right, rightTypeAsNullableType); + + left = Expression.Condition( + ce.Test, + Expression.Convert(ce.IfTrue, rightTypeAsNullableType), + Expression.Convert(_nullExpression, rightTypeAsNullableType) + ); + + return true; + } + left = Expression.Condition( - Expression.Equal(left, Expression.Constant(null, typeof(object))), + Expression.Equal(left, _nullExpression), GenerateDefaultExpression(right.Type), Expression.Convert(left, right.Type) ); } else if (right.Type == typeof(object)) { + if (TryGetAsIndexerExpression(right, out var ce)) + { + var leftTypeAsNullableType = TypeHelper.GetNullableType(left.Type); + + left = Expression.Convert(left, leftTypeAsNullableType); + + right = Expression.Condition( + ce.Test, + Expression.Convert(ce.IfTrue, leftTypeAsNullableType), + Expression.Convert(_nullExpression, leftTypeAsNullableType) + ); + + return true; + } + right = Expression.Condition( - Expression.Equal(right, Expression.Constant(null, typeof(object))), + Expression.Equal(right, _nullExpression), GenerateDefaultExpression(left.Type), Expression.Convert(right, left.Type) ); @@ -546,4 +577,17 @@ private static object[] ConvertIfIEnumerableHasValues(IEnumerable? input) return []; } + + private static bool TryGetAsIndexerExpression(Expression expression, [NotNullWhen(true)] out ConditionalExpression? indexerExpresion) + { + indexerExpresion = expression as ConditionalExpression; + if (indexerExpresion == null) + { + return false; + } + + return + indexerExpresion.IfTrue.ToString().Contains(DynamicClass.IndexerName) && + indexerExpresion.Test.ToString().Contains("ContainsProperty"); + } } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index b0fb8157..b8091f55 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1921,11 +1921,35 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string? if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck) { - var indexerName = TypeHelper.IsDynamicClass(type!) ? DynamicClass.IndexerName : "Item"; + var isDynamicClass = TypeHelper.IsDynamicClass(type!); + var indexerName = isDynamicClass ? DynamicClass.IndexerName : "Item"; + + // Try to get the indexer property "Item" or "DynamicClass_Indexer" which takes a string as parameter var indexerMethod = expression?.Type.GetMethod($"get_{indexerName}", [typeof(string)]); if (indexerMethod != null) { - return Expression.Call(expression, indexerMethod, Expression.Constant(id)); + if (!isDynamicClass) + { + return Expression.Call(expression, indexerMethod, Expression.Constant(id)); + } + + var containsPropertyMethod = typeof(DynamicClass).GetMethod("ContainsProperty"); + if (containsPropertyMethod == null) + { + return Expression.Call(expression, indexerMethod, Expression.Constant(id)); + } + + var callContainsPropertyExpression = Expression.Call( + expression!, + containsPropertyMethod, + Expression.Constant(id) + ); + + return Expression.Condition( + Expression.Equal(callContainsPropertyExpression, Expression.Constant(true)), + Expression.Call(expression, indexerMethod, Expression.Constant(id)), + Expression.Constant(null) + ); } } diff --git a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs index 798ff097..f4bbc731 100644 --- a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs @@ -149,7 +149,7 @@ public Expression ParseIntegerLiteral(int tokenPosition, string text) throw new ParseException(Res.MinusCannotBeAppliedToUnsignedInteger, tokenPosition); } - if (value <= int.MaxValue) + if (value >= int.MinValue && value <= int.MaxValue) { return _constantExpressionHelper.CreateLiteral((int)value, textOriginal); } diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index e391ed29..759f156f 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 } ] """; @@ -516,12 +517,14 @@ public void Where_With_Select() [InlineData("notExisting == \"1\"")] [InlineData("notExisting == \"something\"")] [InlineData("notExisting > 1")] + [InlineData("notExisting < 1")] [InlineData("true == notExisting")] [InlineData("\"true\" == notExisting")] [InlineData("1 == notExisting")] [InlineData("\"1\" == notExisting")] [InlineData("\"something\" == notExisting")] [InlineData("1 < notExisting")] + [InlineData("1 > notExisting")] public void Where_NonExistingMember_EmptyResult(string predicate) { // Arrange @@ -567,6 +570,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..9a81f76a 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] @@ -544,12 +546,14 @@ public void Where_With_Select() [InlineData("notExisting == \"1\"")] [InlineData("notExisting == \"something\"")] [InlineData("notExisting > 1")] + [InlineData("notExisting < 1")] [InlineData("true == notExisting")] [InlineData("\"true\" == notExisting")] [InlineData("1 == notExisting")] [InlineData("\"1\" == notExisting")] [InlineData("\"something\" == notExisting")] [InlineData("1 < notExisting")] + [InlineData("1 > notExisting")] public void Where_NonExistingMember_EmptyResult(string predicate) { // Act diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs index f8ccc496..2f8a8ed8 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs @@ -1,8 +1,8 @@ -using FluentAssertions; using System.Collections.Generic; using System.Globalization; using System.Linq.Dynamic.Core.Parser; using System.Linq.Expressions; +using FluentAssertions; using Xunit; namespace System.Linq.Dynamic.Core.Tests.Parser; @@ -129,6 +129,8 @@ public void NumberParser_ParseNumber_Double(string? culture, string text, double [Theory] [InlineData("42", 42)] [InlineData("-42", -42)] + [InlineData("3000000000", 3000000000)] + [InlineData("-3000000000", -3000000000)] [InlineData("77u", 77)] [InlineData("77l", 77)] [InlineData("77ul", 77)] diff --git a/version.xml b/version.xml index c8f45c09..d0d14392 100644 --- a/version.xml +++ b/version.xml @@ -1,5 +1,5 @@ - 0 + 1 \ No newline at end of file