diff --git a/JsonLogic.Net.UnitTests/JsonLogicTests.cs b/JsonLogic.Net.UnitTests/JsonLogicTests.cs index 8209b61..90f9150 100644 --- a/JsonLogic.Net.UnitTests/JsonLogicTests.cs +++ b/JsonLogic.Net.UnitTests/JsonLogicTests.cs @@ -90,6 +90,19 @@ public JsonLogicTests(ITestOutputHelper output) [InlineData("{`>=`: [3, 1, 1]}", true)] [InlineData("{`>=`: [3, 4, 1]}", false)] + [InlineData("{`<`: [`2020-01-31`, `2020-02-01`, `2020-02-02`]}", true)] + [InlineData("{`<`: [`2020-01-31`, `2020-02-02`, `2020-02-02`]}", false)] + [InlineData("{`<`: [`2020-01-31`, `2020-02-03`, `2020-02-02`]}", false)] + [InlineData("{`<=`: [`2020-01-31`, `2020-02-01`, `2020-02-02`]}", true)] + [InlineData("{`<=`: [`2020-01-31`, `2020-02-02`, `2020-02-02`]}", true)] + [InlineData("{`<=`: [`2020-01-31`, `2020-02-03`, `2020-02-02`]}", false)] + [InlineData("{`>`: [`2020-02-02`, `2020-02-01`, `2020-01-31`]}", true)] + [InlineData("{`>`: [`2020-02-02`, `2020-01-31`, `2020-01-31`]}", false)] + [InlineData("{`>`: [`2020-02-02`, `2020-02-03`, `2020-01-31`]}", false)] + [InlineData("{`>=`: [`2020-02-02`, `2020-02-01`, `2020-01-31`]}", true)] + [InlineData("{`>=`: [`2020-02-02`, `2020-01-31`, `2020-01-31`]}", true)] + [InlineData("{`>=`: [`2020-02-02`, `2020-02-03`, `2020-01-31`]}", false)] + [InlineData("{`var`: `name`}", "John Doe")] [InlineData("{`var`: `address.zip`}", "33333")] [InlineData("{`var`: [`nonexistent`, `default-value`]}", "default-value")] diff --git a/JsonLogic.Net.UnitTests/jsonlogic.com_tests.json b/JsonLogic.Net.UnitTests/jsonlogic.com_tests.json index 01d6416..a0fe5a7 100644 --- a/JsonLogic.Net.UnitTests/jsonlogic.com_tests.json +++ b/JsonLogic.Net.UnitTests/jsonlogic.com_tests.json @@ -1101,5 +1101,52 @@ }, null, "apple" + ], + [ + { + "<": [ + "2020-01-30", + { + "var": "sys.date" + }, + "2020-02-01" + ] + }, + { + "sys": { + "date": "2020-02-01" + } + }, + false + ], + [ + { + "<": [ + "2020-01-30", + { + "var": "sys.date" + }, + "2020-02-01" + ] + }, + { + "sys": { + "date": "2020-01-31" + } + }, + true + ], + [ + { + "<": [ + "A", + "B", + "C", + "D", + "E" + ] + }, + null, + true ] ] \ No newline at end of file diff --git a/JsonLogic.Net/EvaluateOperators.cs b/JsonLogic.Net/EvaluateOperators.cs index 9b689a3..d71c0ba 100644 --- a/JsonLogic.Net/EvaluateOperators.cs +++ b/JsonLogic.Net/EvaluateOperators.cs @@ -75,13 +75,13 @@ private void AddDefaultOperations() AddOperator("min", (p, args, data) => args.Select(a => Convert.ToDouble(p.Apply(a, data))) .Aggregate((prev, next) => (prev < next) ? prev : next)); - AddOperator("<", DoubleArgsSatisfy((prev, next) => prev < next)); - - AddOperator("<=", DoubleArgsSatisfy((prev, next) => prev <= next)); + AddOperator("<", GenericArgsSatisfy((prev, next) => prev < next, (prev, next) => string.CompareOrdinal(prev, next) < 0)); - AddOperator(">", DoubleArgsSatisfy((prev, next) => prev > next)); + AddOperator("<=", GenericArgsSatisfy((prev, next) => prev <= next, (prev, next) => string.CompareOrdinal(prev, next) <= 0)); + + AddOperator(">", GenericArgsSatisfy((prev, next) => prev > next, (prev, next) => string.CompareOrdinal(prev, next) > 0)); - AddOperator(">=", DoubleArgsSatisfy((prev, next) => prev >= next)); + AddOperator(">=", GenericArgsSatisfy((prev, next) => prev >= next, (prev, next) => string.CompareOrdinal(prev, next) >= 0)); AddOperator("var", (p, args, data) => { if (args.Count() == 0) return data; @@ -300,19 +300,37 @@ private Type DictionaryType(object d) { return d.GetType().GetTypeInfo().ImplementedInterfaces.FirstOrDefault(t => t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)); } - - // private Type EnumerableType(object d) - // { - // return d.GetType().GetTypeInfo().ImplementedInterfaces.FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); - // } - - private Func DoubleArgsSatisfy(Func criteria) + + private Func GenericArgsSatisfy(Func criteriaDouble, Func criteriaText) { - return (p, args, data) => { - var values = args.Select(a => a == null ? 0d : Double.Parse(p.Apply(a, data).ToString())).ToArray(); - for (int i = 1; i < values.Length; i++) { - if (!criteria(values[i-1], values[i])) return false; + return (p, args, data) => + { + var values = args + .Where(a => a != null) + .Select(a => p.Apply(a, data)) + .Select(a => JToken.FromObject(a)) + .ToArray(); + + // all values text? + var allText = values.All(a => a.Type == JTokenType.String); + if (allText) + { + var valuesText = args.Select(a => a == null ? "" : p.Apply(a, data).ToString()).ToArray(); + for (int i = 1; i < valuesText.Length; i++) { + + if (!criteriaText(valuesText[i-1], valuesText[i])) return false; + } + + return true; + } + + // not all values are of type text, assume these are Doubles or any other type and therefore handle this as before + var valuesDouble = values.Select(a => a == null ? 0d : Double.Parse(p.Apply(a, data).ToString())).ToArray(); + for (int i = 1; i < valuesDouble.Length; i++) { + + if (!criteriaDouble(valuesDouble[i-1], valuesDouble[i])) return false; } + return true; }; }