diff --git a/json-path/src/main/java/com/jayway/jsonpath/Option.java b/json-path/src/main/java/com/jayway/jsonpath/Option.java index 412703fd..30be27e0 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Option.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Option.java @@ -83,6 +83,11 @@ public enum Option { * If REQUIRE_PROPERTIES option is present PathNotFoundException is thrown. * If REQUIRE_PROPERTIES option is not present ["b-val"] is returned. */ - REQUIRE_PROPERTIES + REQUIRE_PROPERTIES, + /** + * Configures JsonPath to implicitly convert strings to numbers for comparisons in filters, + * mimicking behavior in javascript, e.g. 9 < "73" + */ + IMPLICIT_NUMERIC_CONVERSIONS } diff --git a/json-path/src/main/java/com/jayway/jsonpath/Predicate.java b/json-path/src/main/java/com/jayway/jsonpath/Predicate.java index 1d19991e..c125f73a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Predicate.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Predicate.java @@ -14,6 +14,7 @@ */ package com.jayway.jsonpath; +import com.jayway.jsonpath.internal.filter.EvaluatorFactory; import com.jayway.jsonpath.spi.mapper.MappingException; /** @@ -49,5 +50,11 @@ public interface Predicate { * @return configuration */ Configuration configuration(); + + /** + * Factory to use for creating evaluators for filtering. + * @return Factory to use for creating evaluators for filtering. + */ + EvaluatorFactory evaluatorFactory(); } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/DefaultEvaluatorFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/DefaultEvaluatorFactory.java new file mode 100644 index 00000000..df6c7342 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/DefaultEvaluatorFactory.java @@ -0,0 +1,268 @@ +package com.jayway.jsonpath.internal.filter; + +import com.jayway.jsonpath.JsonPathException; +import com.jayway.jsonpath.Predicate; + +import java.util.EnumMap; + +public class DefaultEvaluatorFactory implements EvaluatorFactory { + + private static final EnumMap evaluators + = new EnumMap(RelationalOperator.class); + + static { + evaluators.put(RelationalOperator.EXISTS, new ExistsEvaluator()); + evaluators.put(RelationalOperator.NE, new NotEqualsEvaluator()); + evaluators.put(RelationalOperator.TSNE, new TypeSafeNotEqualsEvaluator()); + evaluators.put(RelationalOperator.EQ, new EqualsEvaluator()); + evaluators.put(RelationalOperator.TSEQ, new TypeSafeEqualsEvaluator()); + evaluators.put(RelationalOperator.LT, new LessThanEvaluator()); + evaluators.put(RelationalOperator.LTE, new LessThanEqualsEvaluator()); + evaluators.put(RelationalOperator.GT, new GreaterThanEvaluator()); + evaluators.put(RelationalOperator.GTE, new GreaterThanEqualsEvaluator()); + evaluators.put(RelationalOperator.REGEX, new RegexpEvaluator()); + evaluators.put(RelationalOperator.SIZE, new SizeEvaluator()); + evaluators.put(RelationalOperator.EMPTY, new EmptyEvaluator()); + evaluators.put(RelationalOperator.IN, new InEvaluator()); + evaluators.put(RelationalOperator.NIN, new NotInEvaluator()); + evaluators.put(RelationalOperator.ALL, new AllEvaluator()); + evaluators.put(RelationalOperator.CONTAINS, new ContainsEvaluator()); + evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator()); + evaluators.put(RelationalOperator.TYPE, new TypeEvaluator()); + } + + @Override + public Evaluator createEvaluator(RelationalOperator operator) { + return evaluators.get(operator); + } + + private static class ExistsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(!left.isBooleanNode() && !right.isBooleanNode()){ + throw new JsonPathException("Failed to evaluate exists expression"); + } + return left.asBooleanNode().getBoolean() == right.asBooleanNode().getBoolean(); + } + } + + private static class NotEqualsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + return !evaluators.get(RelationalOperator.EQ).evaluate(left, right, ctx); + } + } + + private static class TypeSafeNotEqualsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + return !evaluators.get(RelationalOperator.TSEQ).evaluate(left, right, ctx); + } + } + + private static class EqualsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isJsonNode() && right.isJsonNode()){ + return left.asJsonNode().equals(right.asJsonNode(), ctx); + } else { + return left.equals(right); + } + } + } + + private static class TypeSafeEqualsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(!left.getClass().equals(right.getClass())){ + return false; + } + return evaluators.get(RelationalOperator.EQ).evaluate(left, right, ctx); + } + } + + private static class TypeEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + return right.asClassNode().getClazz() == left.type(ctx); + } + } + + private static class LessThanEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isNumberNode() && right.isNumberNode()){ + return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) < 0; + } if(left.isStringNode() && right.isStringNode()){ + return left.asStringNode().getString().compareTo(right.asStringNode().getString()) < 0; + } + return false; + } + } + + private static class LessThanEqualsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isNumberNode() && right.isNumberNode()){ + return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) <= 0; + } if(left.isStringNode() && right.isStringNode()){ + return left.asStringNode().getString().compareTo(right.asStringNode().getString()) <= 0; + } + return false; + } + } + + private static class GreaterThanEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isNumberNode() && right.isNumberNode()){ + return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) > 0; + } else if(left.isStringNode() && right.isStringNode()){ + return left.asStringNode().getString().compareTo(right.asStringNode().getString()) > 0; + } + return false; + } + } + + private static class GreaterThanEqualsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isNumberNode() && right.isNumberNode()){ + return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) >= 0; + } else if(left.isStringNode() && right.isStringNode()){ + return left.asStringNode().getString().compareTo(right.asStringNode().getString()) >= 0; + } + return false; + } + } + + private static class SizeEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if (! right.isNumberNode()) { + return false; + } + int expectedSize = right.asNumberNode().getNumber().intValue(); + + if(left.isStringNode()){ + return left.asStringNode().length() == expectedSize; + } else if(left.isJsonNode()){ + return left.asJsonNode().length(ctx) == expectedSize; + } + return false; + } + } + + private static class EmptyEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isStringNode()){ + return left.asStringNode().isEmpty() == right.asBooleanNode().getBoolean(); + } else if(left.isJsonNode()){ + return left.asJsonNode().isEmpty(ctx) == right.asBooleanNode().getBoolean(); + } + return false; + } + } + + private static class InEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + ValueNode.ValueListNode valueListNode; + if(right.isJsonNode()){ + ValueNode vn = right.asJsonNode().asValueListNode(ctx); + if(vn.isUndefinedNode()){ + return false; + } else { + valueListNode = vn.asValueListNode(); + } + } else { + valueListNode = right.asValueListNode(); + } + return valueListNode.contains(left); + } + } + + private static class NotInEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + return !evaluators.get(RelationalOperator.IN).evaluate(left, right, ctx); + } + } + + private static class AllEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + ValueNode.ValueListNode requiredValues = right.asValueListNode(); + + if(left.isJsonNode()){ + ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); //returns UndefinedNode if conversion is not possible + if(valueNode.isValueListNode()){ + ValueNode.ValueListNode shouldContainAll = valueNode.asValueListNode(); + for (ValueNode required : requiredValues) { + if(!shouldContainAll.contains(required)){ + return false; + } + } + } + return true; + } + return false; + } + } + + private static class ContainsEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(left.isStringNode() && right.isStringNode()){ + return left.asStringNode().contains(right.asStringNode().getString()); + } else if(left.isJsonNode()){ + ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); + if(valueNode.isUndefinedNode()) return false; + else { + boolean res = valueNode.asValueListNode().contains(right); + return res; + } + } + return false; + } + } + + private static class PredicateMatchEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + return right.asPredicateNode().getPredicate().apply(ctx); + } + } + + private static class RegexpEvaluator implements Evaluator { + @Override + public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { + if(!(left.isPatternNode() ^ right.isPatternNode())){ + return false; + } + + if (left.isPatternNode()) { + return matches(left.asPatternNode(), getInput(right)); + } else { + return matches(right.asPatternNode(), getInput(left)); + } + } + + private boolean matches(ValueNode.PatternNode patternNode, String inputToMatch) { + return patternNode.getCompiledPattern().matcher(inputToMatch).matches(); + } + + private String getInput(ValueNode valueNode) { + String input = ""; + + if (valueNode.isStringNode() || valueNode.isNumberNode()) { + input = valueNode.asStringNode().getString(); + } else if (valueNode.isBooleanNode()) { + input = valueNode.asBooleanNode().toString(); + } + + return input; + } + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java index 6831920c..6dd1544a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java @@ -1,267 +1,9 @@ +// (c) Copyright 2016 Likelihood, Inc. package com.jayway.jsonpath.internal.filter; -import com.jayway.jsonpath.JsonPathException; -import com.jayway.jsonpath.Predicate; - -import java.util.HashMap; -import java.util.Map; - -public class EvaluatorFactory { - - private static final Map evaluators = new HashMap(); - - static { - evaluators.put(RelationalOperator.EXISTS, new ExistsEvaluator()); - evaluators.put(RelationalOperator.NE, new NotEqualsEvaluator()); - evaluators.put(RelationalOperator.TSNE, new TypeSafeNotEqualsEvaluator()); - evaluators.put(RelationalOperator.EQ, new EqualsEvaluator()); - evaluators.put(RelationalOperator.TSEQ, new TypeSafeEqualsEvaluator()); - evaluators.put(RelationalOperator.LT, new LessThanEvaluator()); - evaluators.put(RelationalOperator.LTE, new LessThanEqualsEvaluator()); - evaluators.put(RelationalOperator.GT, new GreaterThanEvaluator()); - evaluators.put(RelationalOperator.GTE, new GreaterThanEqualsEvaluator()); - evaluators.put(RelationalOperator.REGEX, new RegexpEvaluator()); - evaluators.put(RelationalOperator.SIZE, new SizeEvaluator()); - evaluators.put(RelationalOperator.EMPTY, new EmptyEvaluator()); - evaluators.put(RelationalOperator.IN, new InEvaluator()); - evaluators.put(RelationalOperator.NIN, new NotInEvaluator()); - evaluators.put(RelationalOperator.ALL, new AllEvaluator()); - evaluators.put(RelationalOperator.CONTAINS, new ContainsEvaluator()); - evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator()); - evaluators.put(RelationalOperator.TYPE, new TypeEvaluator()); - } - - public static Evaluator createEvaluator(RelationalOperator operator){ - return evaluators.get(operator); - } - - private static class ExistsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(!left.isBooleanNode() && !right.isBooleanNode()){ - throw new JsonPathException("Failed to evaluate exists expression"); - } - return left.asBooleanNode().getBoolean() == right.asBooleanNode().getBoolean(); - } - } - - private static class NotEqualsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - return !evaluators.get(RelationalOperator.EQ).evaluate(left, right, ctx); - } - } - - private static class TypeSafeNotEqualsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - return !evaluators.get(RelationalOperator.TSEQ).evaluate(left, right, ctx); - } - } - - private static class EqualsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isJsonNode() && right.isJsonNode()){ - return left.asJsonNode().equals(right.asJsonNode(), ctx); - } else { - return left.equals(right); - } - } - } - - private static class TypeSafeEqualsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(!left.getClass().equals(right.getClass())){ - return false; - } - return evaluators.get(RelationalOperator.EQ).evaluate(left, right, ctx); - } - } - - private static class TypeEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - return right.asClassNode().getClazz() == left.type(ctx); - } - } - - private static class LessThanEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isNumberNode() && right.isNumberNode()){ - return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) < 0; - } if(left.isStringNode() && right.isStringNode()){ - return left.asStringNode().getString().compareTo(right.asStringNode().getString()) < 0; - } - return false; - } - } - - private static class LessThanEqualsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isNumberNode() && right.isNumberNode()){ - return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) <= 0; - } if(left.isStringNode() && right.isStringNode()){ - return left.asStringNode().getString().compareTo(right.asStringNode().getString()) <= 0; - } - return false; - } - } - - private static class GreaterThanEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isNumberNode() && right.isNumberNode()){ - return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) > 0; - } else if(left.isStringNode() && right.isStringNode()){ - return left.asStringNode().getString().compareTo(right.asStringNode().getString()) > 0; - } - return false; - } - } - - private static class GreaterThanEqualsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isNumberNode() && right.isNumberNode()){ - return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) >= 0; - } else if(left.isStringNode() && right.isStringNode()){ - return left.asStringNode().getString().compareTo(right.asStringNode().getString()) >= 0; - } - return false; - } - } - - private static class SizeEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if (! right.isNumberNode()) { - return false; - } - int expectedSize = right.asNumberNode().getNumber().intValue(); - - if(left.isStringNode()){ - return left.asStringNode().length() == expectedSize; - } else if(left.isJsonNode()){ - return left.asJsonNode().length(ctx) == expectedSize; - } - return false; - } - } - - private static class EmptyEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isStringNode()){ - return left.asStringNode().isEmpty() == right.asBooleanNode().getBoolean(); - } else if(left.isJsonNode()){ - return left.asJsonNode().isEmpty(ctx) == right.asBooleanNode().getBoolean(); - } - return false; - } - } - - private static class InEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode valueListNode; - if(right.isJsonNode()){ - ValueNode vn = right.asJsonNode().asValueListNode(ctx); - if(vn.isUndefinedNode()){ - return false; - } else { - valueListNode = vn.asValueListNode(); - } - } else { - valueListNode = right.asValueListNode(); - } - return valueListNode.contains(left); - } - } - - private static class NotInEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - return !evaluators.get(RelationalOperator.IN).evaluate(left, right, ctx); - } - } - - private static class AllEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode requiredValues = right.asValueListNode(); - - if(left.isJsonNode()){ - ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); //returns UndefinedNode if conversion is not possible - if(valueNode.isValueListNode()){ - ValueNode.ValueListNode shouldContainAll = valueNode.asValueListNode(); - for (ValueNode required : requiredValues) { - if(!shouldContainAll.contains(required)){ - return false; - } - } - } - return true; - } - return false; - } - } - - private static class ContainsEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(left.isStringNode() && right.isStringNode()){ - return left.asStringNode().contains(right.asStringNode().getString()); - } else if(left.isJsonNode()){ - ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); - if(valueNode.isUndefinedNode()) return false; - else { - boolean res = valueNode.asValueListNode().contains(right); - return res; - } - } - return false; - } - } - - private static class PredicateMatchEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - return right.asPredicateNode().getPredicate().apply(ctx); - } - } - - private static class RegexpEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - if(!(left.isPatternNode() ^ right.isPatternNode())){ - return false; - } - - if (left.isPatternNode()) { - return matches(left.asPatternNode(), getInput(right)); - } else { - return matches(right.asPatternNode(), getInput(left)); - } - } - - private boolean matches(ValueNode.PatternNode patternNode, String inputToMatch) { - return patternNode.getCompiledPattern().matcher(inputToMatch).matches(); - } - - private String getInput(ValueNode valueNode) { - String input = ""; - - if (valueNode.isStringNode() || valueNode.isNumberNode()) { - input = valueNode.asStringNode().getString(); - } else if (valueNode.isBooleanNode()) { - input = valueNode.asBooleanNode().toString(); - } - - return input; - } - } +/** + * Interface for creating evaluators. + */ +public interface EvaluatorFactory { + Evaluator createEvaluator(RelationalOperator operator); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ImplicitNumericConversionEvaluatorFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ImplicitNumericConversionEvaluatorFactory.java new file mode 100644 index 00000000..bbcdf851 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ImplicitNumericConversionEvaluatorFactory.java @@ -0,0 +1,60 @@ +// (c) Copyright 2016 Likelihood, Inc. +package com.jayway.jsonpath.internal.filter; + +import java.util.EnumMap; + +import com.jayway.jsonpath.Predicate.PredicateContext; +import com.jayway.jsonpath.internal.filter.ValueNode.NumberNode; + +/** + * Factory that supports implicit numeric conversions for evaluators. + */ +public class ImplicitNumericConversionEvaluatorFactory implements EvaluatorFactory { + + private final EnumMap + evaluators = new EnumMap(RelationalOperator.class); + + public ImplicitNumericConversionEvaluatorFactory(EvaluatorFactory delegateFactory) { + // for each operator, wrap just the ones that allow implicit numeric conversion. + for (RelationalOperator op : RelationalOperator.values()) { + Evaluator delegateEvaluator = delegateFactory.createEvaluator(op); + if (delegateEvaluator != null) { + evaluators.put( + op, + op.allowImplicitNumericConversion() + ? new Wrapper(delegateEvaluator) + : delegateEvaluator); + } + } + } + + @Override + public Evaluator createEvaluator(RelationalOperator operator) { + return evaluators.get(operator); + } + + private static class Wrapper implements Evaluator { + Evaluator wrapped; + + Wrapper(Evaluator wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean evaluate( + ValueNode left, ValueNode right, PredicateContext ctx) { + final boolean eval = wrapped.evaluate(left, right, ctx); + if (!eval) { + if (left.isNumberNode() && right.isStringNode()) { + final ValueNode converted = right.asNumberNode(); + return (converted != NumberNode.NAN) && wrapped.evaluate(left, converted, ctx); + } + if (left.isStringNode() && right.isNumberNode()) { + final ValueNode converted = left.asNumberNode(); + return (converted != NumberNode.NAN) && wrapped.evaluate(converted, right, ctx); + } + } + return eval; + } + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalExpressionNode.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalExpressionNode.java index 05d8adb5..99b6686e 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalExpressionNode.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalExpressionNode.java @@ -39,8 +39,9 @@ public class RelationalExpressionNode extends ExpressionNode { if(right.isPathNode()){ r = right.asPathNode().evaluate(ctx); } - Evaluator evaluator = EvaluatorFactory.createEvaluator(relationalOperator); - if(evaluator != null){ + + Evaluator evaluator = ctx.evaluatorFactory().createEvaluator(relationalOperator); + if(evaluator != null) { return evaluator.evaluate(l, r, ctx); } return false; diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java index 4575f60b..a7d894ad 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java @@ -4,8 +4,9 @@ import com.jayway.jsonpath.InvalidPathException; public enum RelationalOperator { - GTE(">="), - LTE("<="), + GTE(">=", true), + LTE("<=", true), + // EQ already support implicit numeric conversion in StringNode.equals and NumericNode.equals EQ("=="), /** @@ -18,8 +19,8 @@ public enum RelationalOperator { * Type safe not equals */ TSNE("!=="), - LT("<"), - GT(">"), + LT("<", true), + GT(">", true), REGEX("=~"), NIN("NIN"), IN("IN"), @@ -32,9 +33,14 @@ public enum RelationalOperator { EMPTY("EMPTY"); private final String operatorString; + private final boolean allowImplicitNumericConversion; RelationalOperator(String operatorString) { + this(operatorString, false); + } + RelationalOperator(String operatorString, boolean allowImplicitNumericConversion) { this.operatorString = operatorString; + this.allowImplicitNumericConversion = allowImplicitNumericConversion; } public static RelationalOperator fromString(String operatorString){ @@ -46,8 +52,21 @@ public enum RelationalOperator { throw new InvalidPathException("Filter operator " + operatorString + " is not supported!"); } + public boolean allowImplicitNumericConversion() { + return allowImplicitNumericConversion; + } + @Override public String toString() { return operatorString; } + + public static final EvaluatorFactory DEFAULT_EVALUATOR_FACTORY = new DefaultEvaluatorFactory(); + public static final EvaluatorFactory IMPLICIT_NUMERIC_CONVERSION_EVALUATOR_FACTORY + = new ImplicitNumericConversionEvaluatorFactory(DEFAULT_EVALUATOR_FACTORY); + + public static EvaluatorFactory getEvaluatorFactory(boolean allowImplicitNumericConversion) { + return allowImplicitNumericConversion + ? IMPLICIT_NUMERIC_CONVERSION_EVALUATOR_FACTORY : DEFAULT_EVALUATOR_FACTORY; + } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java index d471b6ce..d23da16b 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java @@ -15,8 +15,11 @@ package com.jayway.jsonpath.internal.path; import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.Option; import com.jayway.jsonpath.Predicate; import com.jayway.jsonpath.internal.Path; +import com.jayway.jsonpath.internal.filter.EvaluatorFactory; +import com.jayway.jsonpath.internal.filter.RelationalOperator; import com.jayway.jsonpath.spi.mapper.MappingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,12 +34,15 @@ public class PredicateContextImpl implements Predicate.PredicateContext { private final Object rootDocument; private final Configuration configuration; private final HashMap documentPathCache; + private final EvaluatorFactory evaluatorFactory; public PredicateContextImpl(Object contextDocument, Object rootDocument, Configuration configuration, HashMap documentPathCache) { this.contextDocument = contextDocument; this.rootDocument = rootDocument; this.configuration = configuration; this.documentPathCache = documentPathCache; + this.evaluatorFactory = RelationalOperator.getEvaluatorFactory( + configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); } public Object evaluate(Path path){ @@ -79,4 +85,8 @@ public class PredicateContextImpl implements Predicate.PredicateContext { return configuration; } + @Override + public EvaluatorFactory evaluatorFactory() { + return evaluatorFactory; + } } \ No newline at end of file diff --git a/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java b/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java index 714aa92b..c03dae42 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java @@ -119,7 +119,10 @@ public class BaseTest { "}"; public Predicate.PredicateContext createPredicateContext(final Object check) { + return createPredicateContext(check, Configuration.defaultConfiguration()); + } - return new PredicateContextImpl(check, check, Configuration.defaultConfiguration(), new HashMap()); + public Predicate.PredicateContext createPredicateContext(final Object check, Configuration configuration) { + return new PredicateContextImpl(check, check, configuration, new HashMap()); } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java index a9c02e76..1d7470b0 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java @@ -1,10 +1,10 @@ package com.jayway.jsonpath; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.regex.Pattern; import static com.jayway.jsonpath.Criteria.where; @@ -12,10 +12,27 @@ import static com.jayway.jsonpath.Filter.filter; import static com.jayway.jsonpath.Filter.parse; import static org.assertj.core.api.Assertions.assertThat; +@RunWith(Parameterized.class) public class FilterTest extends BaseTest { + private final Configuration configuration; + private Object json; - Object json = Configuration.defaultConfiguration().jsonProvider().parse( + public FilterTest(Configuration configuration) { + this.configuration = configuration; + json = configuration.jsonProvider().parse(jsonString); + } + + // Run test with configurations with implicit numeric conversions turned off and on. + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { + { Configuration.defaultConfiguration() }, + { Configuration.defaultConfiguration().addOptions(Option.IMPLICIT_NUMERIC_CONVERSIONS) }, + }); + } + + private String jsonString = "{" + " \"int-key\" : 1, " + " \"long-key\" : 3000000000, " + @@ -24,12 +41,12 @@ public class FilterTest extends BaseTest { " \"null-key\" : null, " + " \"string-key\" : \"string\", " + " \"string-key-empty\" : \"\", " + + " \"string-numeric-key\": \"10\", " + " \"char-key\" : \"c\", " + " \"arr-empty\" : [], " + " \"int-arr\" : [0,1,2,3,4], " + " \"string-arr\" : [\"a\",\"b\",\"c\",\"d\",\"e\"] " + - "}" - ); + "}"; //---------------------------------------------------------------------------- // @@ -38,55 +55,62 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_eq_evals() { - assertThat(filter(where("int-key").eq(1)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-key").eq(666)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-key").eq(1)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-key").eq(666)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void int_eq_string_evals() { - assertThat(filter(where("int-key").eq("1")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-key").eq("666")).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-key").eq("1")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-key").eq("666")).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(Filter.parse("[?(1 == '1')]").apply(createPredicateContext(json))).isEqualTo(true); - assertThat(Filter.parse("[?('1' == 1)]").apply(createPredicateContext(json))).isEqualTo(true); + assertThat(Filter.parse("[?(1 == '1')]").apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(Filter.parse("[?('1' == 1)]").apply(createPredicateContext(json, configuration))).isEqualTo(true); - assertThat(Filter.parse("[?(1 === '1')]").apply(createPredicateContext(json))).isEqualTo(false); - assertThat(Filter.parse("[?('1' === 1)]").apply(createPredicateContext(json))).isEqualTo(false); + assertThat(Filter.parse("[?(1 === '1')]").apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(Filter.parse("[?('1' === 1)]").apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(Filter.parse("[?(1 === 1)]").apply(createPredicateContext(json))).isEqualTo(true); + assertThat(Filter.parse("[?(1 === 1)]").apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void long_eq_evals() { - assertThat(filter(where("long-key").eq(3000000000L)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("long-key").eq(666L)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("long-key").eq(3000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("long-key").eq(666L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void double_eq_evals() { - assertThat(filter(where("double-key").eq(10.1D)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("double-key").eq(10.10D)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("double-key").eq(10.11D)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("double-key").eq(10.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").eq(10.10D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").eq(10.11D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void string_eq_evals() { - assertThat(filter(where("string-key").eq("string")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").eq("666")).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").eq("string")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").eq("666")).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void boolean_eq_evals() { - assertThat(filter(where("boolean-key").eq(true)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("boolean-key").eq(false)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("boolean-key").eq(true)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("boolean-key").eq(false)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void null_eq_evals() { - assertThat(filter(where("null-key").eq(null)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("null-key").eq("666")).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("string-key").eq(null)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("null-key").eq(null)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("null-key").eq("666")).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("string-key").eq(null)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + } + + @Test + public void implicit_numeric_conversion_eq_evals() { + // equals supports implicit numeric conversion in all cases. + assertThat(filter(where("string-numeric-key").eq(10L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").eq("10.1")).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test @@ -103,40 +127,47 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_ne_evals() { - assertThat(filter(where("int-key").ne(1)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("int-key").ne(666)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("int-key").ne(1)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("int-key").ne(666)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void long_ne_evals() { - assertThat(filter(where("long-key").ne(3000000000L)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("long-key").ne(666L)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("long-key").ne(3000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("long-key").ne(666L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void double_ne_evals() { - assertThat(filter(where("double-key").ne(10.1D)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("double-key").ne(10.10D)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("double-key").ne(10.11D)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("double-key").ne(10.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("double-key").ne(10.10D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("double-key").ne(10.11D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void string_ne_evals() { - assertThat(filter(where("string-key").ne("string")).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("string-key").ne("666")).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("string-key").ne("string")).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("string-key").ne("666")).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void boolean_ne_evals() { - assertThat(filter(where("boolean-key").ne(true)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("boolean-key").ne(false)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("boolean-key").ne(true)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("boolean-key").ne(false)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void null_ne_evals() { - assertThat(filter(where("null-key").ne(null)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("null-key").ne("666")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").ne(null)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("null-key").ne(null)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("null-key").ne("666")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").ne(null)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + } + + @Test + public void implicit_numeric_conversion_ne_evals() { + // equals supports implicit numeric conversion in all cases, so ne does as well. + assertThat(filter(where("string-numeric-key").ne(10L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("double-key").ne("10.1")).apply(createPredicateContext(json, configuration))).isEqualTo(false); } //---------------------------------------------------------------------------- @@ -146,26 +177,38 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_lt_evals() { - assertThat(filter(where("int-key").lt(10)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-key").lt(0)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-key").lt(10)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-key").lt(0)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void long_lt_evals() { - assertThat(filter(where("long-key").lt(4000000000L)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("long-key").lt(666L)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("long-key").lt(4000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("long-key").lt(666L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void double_lt_evals() { - assertThat(filter(where("double-key").lt(100.1D)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("double-key").lt(1.1D)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("double-key").lt(100.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").lt(1.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void string_lt_evals() { - assertThat(filter(where("char-key").lt("x")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("char-key").lt("a")).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("char-key").lt("x")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("char-key").lt("a")).apply(createPredicateContext(json, configuration))).isEqualTo(false); + } + + @Test + public void implicit_numeric_conversion_lt_evals() { + assertThat(filter(where("string-numeric-key").lt(100L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("string-numeric-key").lt(1L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); + assertThat(filter(where("double-key").lt("100.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("double-key").lt("1.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); } //---------------------------------------------------------------------------- @@ -175,23 +218,39 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_lte_evals() { - assertThat(filter(where("int-key").lte(10)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-key").lte(1)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-key").lte(0)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-key").lte(10)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-key").lte(1)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-key").lte(0)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void long_lte_evals() { - assertThat(filter(where("long-key").lte(4000000000L)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("long-key").lte(3000000000L)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("long-key").lte(666L)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("long-key").lte(4000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("long-key").lte(3000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("long-key").lte(666L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void double_lte_evals() { - assertThat(filter(where("double-key").lte(100.1D)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("double-key").lte(10.1D)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("double-key").lte(1.1D)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("double-key").lte(100.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").lte(10.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").lte(1.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + } + + @Test + public void implicit_numeric_conversion_lte_evals() { + assertThat(filter(where("string-numeric-key").lte(100L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("string-numeric-key").lte(10L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("string-numeric-key").lte(1L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); + assertThat(filter(where("double-key").lte("100.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("double-key").lte("10.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("double-key").lte("1.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); } //---------------------------------------------------------------------------- @@ -201,26 +260,38 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_gt_evals() { - assertThat(filter(where("int-key").gt(10)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("int-key").gt(0)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("int-key").gt(10)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("int-key").gt(0)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void long_gt_evals() { - assertThat(filter(where("long-key").gt(4000000000L)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("long-key").gt(666L)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("long-key").gt(4000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("long-key").gt(666L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void double_gt_evals() { - assertThat(filter(where("double-key").gt(100.1D)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("double-key").gt(1.1D)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("double-key").gt(100.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("double-key").gt(1.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void string_gt_evals() { - assertThat(filter(where("char-key").gt("x")).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("char-key").gt("a")).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("char-key").gt("x")).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("char-key").gt("a")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + } + + @Test + public void implicit_numeric_conversion_gt_evals() { + assertThat(filter(where("string-numeric-key").gt(100L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); + assertThat(filter(where("string-numeric-key").gt(1L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("double-key").gt("100.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); + assertThat(filter(where("double-key").gt("1.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); } //---------------------------------------------------------------------------- @@ -230,23 +301,39 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_gte_evals() { - assertThat(filter(where("int-key").gte(10)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("int-key").gte(1)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-key").gte(0)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("int-key").gte(10)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("int-key").gte(1)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-key").gte(0)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void long_gte_evals() { - assertThat(filter(where("long-key").gte(4000000000L)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("long-key").gte(3000000000L)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("long-key").gte(666L)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("long-key").gte(4000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("long-key").gte(3000000000L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("long-key").gte(666L)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @Test public void double_gte_evals() { - assertThat(filter(where("double-key").gte(100.1D)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("double-key").gte(10.1D)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("double-key").gte(1.1D)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("double-key").gte(100.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("double-key").gte(10.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("double-key").gte(1.1D)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + } + + @Test + public void implicit_numeric_conversion_gte_evals() { + assertThat(filter(where("string-numeric-key").gte(100L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); + assertThat(filter(where("string-numeric-key").gte(10L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("string-numeric-key").gte(1L)).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("double-key").gte("100.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(false); + assertThat(filter(where("double-key").gte("10.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); + assertThat(filter(where("double-key").gte("1.1")).apply(createPredicateContext(json, configuration))) + .isEqualTo(configuration.containsOption(Option.IMPLICIT_NUMERIC_CONVERSIONS)); } //---------------------------------------------------------------------------- @@ -256,10 +343,10 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void string_regex_evals() { - assertThat(filter(where("string-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").regex(Pattern.compile("^tring$"))).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("null-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("int-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").regex(Pattern.compile("^tring$"))).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("null-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("int-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json, configuration))).isEqualTo(false); } //---------------------------------------------------------------------------- @@ -272,7 +359,7 @@ public class FilterTest extends BaseTest { String nest = "{\"a\":true}"; String arr = "[1,2]"; String json = "{\"foo\":" + arr + ", \"bar\":" + nest + "}"; - Object tree = Configuration.defaultConfiguration().jsonProvider().parse(json); + Object tree = configuration.jsonProvider().parse(json); Predicate.PredicateContext context = createPredicateContext(tree); Filter farr = parse("[?(@.foo == " + arr + ")]"); //Filter fobjF = parse("[?(@.foo == " + nest + ")]"); @@ -289,11 +376,11 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void string_in_evals() { - assertThat(filter(where("string-key").in("a", null, "string")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").in("a", null)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("null-key").in("a", null)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("null-key").in("a", "b")).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("string-arr").in("a")).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").in("a", null, "string")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").in("a", null)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("null-key").in("a", null)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("null-key").in("a", "b")).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("string-arr").in("a")).apply(createPredicateContext(json, configuration))).isEqualTo(false); } //---------------------------------------------------------------------------- @@ -303,11 +390,11 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void string_nin_evals() { - assertThat(filter(where("string-key").nin("a", null, "string")).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("string-key").nin("a", null)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("null-key").nin("a", null)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("null-key").nin("a", "b")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-arr").nin("a")).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("string-key").nin("a", null, "string")).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("string-key").nin("a", null)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("null-key").nin("a", null)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("null-key").nin("a", "b")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-arr").nin("a")).apply(createPredicateContext(json, configuration))).isEqualTo(true); } @@ -318,17 +405,17 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void int_all_evals() { - assertThat(filter(where("int-arr").all(0,1)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-arr").all(0,7)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-arr").all(0,1)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-arr").all(0,7)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void string_all_evals() { - assertThat(filter(where("string-arr").all("a","b")).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-arr").all("a","x")).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-arr").all("a","b")).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-arr").all("a","x")).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void not_array_all_evals() { - assertThat(filter(where("string-key").all("a","b")).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").all("a","b")).apply(createPredicateContext(json, configuration))).isEqualTo(false); } //---------------------------------------------------------------------------- @@ -338,24 +425,24 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void array_size_evals() { - assertThat(filter(where("string-arr").size(5)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-arr").size(7)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-arr").size(5)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-arr").size(7)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void string_size_evals() { - assertThat(filter(where("string-key").size(6)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").size(7)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").size(6)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").size(7)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void other_size_evals() { - assertThat(filter(where("int-key").size(6)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-key").size(6)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @Test public void null_size_evals() { - assertThat(filter(where("null-key").size(6)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("null-key").size(6)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } //---------------------------------------------------------------------------- @@ -365,11 +452,11 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void exists_evals() { - assertThat(filter(where("string-key").exists(true)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").exists(false)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").exists(true)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").exists(false)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("missing-key").exists(true)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("missing-key").exists(false)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("missing-key").exists(true)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("missing-key").exists(false)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } //---------------------------------------------------------------------------- @@ -379,15 +466,15 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void type_evals() { - assertThat(filter(where("string-key").type(String.class)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").type(Number.class)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").type(String.class)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").type(Number.class)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("int-key").type(String.class)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("int-key").type(Number.class)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("int-key").type(String.class)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("int-key").type(Number.class)).apply(createPredicateContext(json, configuration))).isEqualTo(true); - assertThat(filter(where("null-key").type(String.class)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("null-key").type(String.class)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("int-arr").type(List.class)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("int-arr").type(List.class)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } //---------------------------------------------------------------------------- @@ -397,13 +484,13 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void not_empty_evals() { - assertThat(filter(where("string-key").notEmpty()).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key-empty").notEmpty()).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").notEmpty()).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key-empty").notEmpty()).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("int-arr").notEmpty()).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("arr-empty").notEmpty()).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-arr").notEmpty()).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("arr-empty").notEmpty()).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("null-key").notEmpty()).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("null-key").notEmpty()).apply(createPredicateContext(json, configuration))).isEqualTo(false); } //---------------------------------------------------------------------------- @@ -413,20 +500,20 @@ public class FilterTest extends BaseTest { //---------------------------------------------------------------------------- @Test public void empty_evals() { - assertThat(filter(where("string-key").empty(false)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key").empty(true)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key").empty(false)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key").empty(true)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("string-key-empty").empty(true)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("string-key-empty").empty(false)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("string-key-empty").empty(true)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("string-key-empty").empty(false)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("int-arr").empty(false)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("int-arr").empty(true)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("int-arr").empty(false)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("int-arr").empty(true)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("arr-empty").empty(true)).apply(createPredicateContext(json))).isEqualTo(true); - assertThat(filter(where("arr-empty").empty(false)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("arr-empty").empty(true)).apply(createPredicateContext(json, configuration))).isEqualTo(true); + assertThat(filter(where("arr-empty").empty(false)).apply(createPredicateContext(json, configuration))).isEqualTo(false); - assertThat(filter(where("null-key").empty(true)).apply(createPredicateContext(json))).isEqualTo(false); - assertThat(filter(where("null-key").empty(false)).apply(createPredicateContext(json))).isEqualTo(false); + assertThat(filter(where("null-key").empty(true)).apply(createPredicateContext(json, configuration))).isEqualTo(false); + assertThat(filter(where("null-key").empty(false)).apply(createPredicateContext(json, configuration))).isEqualTo(false); } @@ -450,7 +537,7 @@ public class FilterTest extends BaseTest { return i == 1; } }; - assertThat(filter(where("string-key").eq("string").and("$").matches(p)).apply(createPredicateContext(json))).isEqualTo(true); + assertThat(filter(where("string-key").eq("string").and("$").matches(p)).apply(createPredicateContext(json, configuration))).isEqualTo(true); } //---------------------------------------------------------------------------- @@ -482,14 +569,14 @@ public class FilterTest extends BaseTest { @Test public void testFilterWithOrShortCircuit1() throws Exception { - Object json = Configuration.defaultConfiguration().jsonProvider().parse( "{\"firstname\":\"Bob\",\"surname\":\"Smith\",\"age\":30}"); - assertThat(Filter.parse("[?((@.firstname == 'Bob' || @.firstname == 'Jane') && @.surname == 'Doe')]").apply(createPredicateContext(json))).isFalse(); + Object json = configuration.jsonProvider().parse( "{\"firstname\":\"Bob\",\"surname\":\"Smith\",\"age\":30}"); + assertThat(Filter.parse("[?((@.firstname == 'Bob' || @.firstname == 'Jane') && @.surname == 'Doe')]").apply(createPredicateContext(json, configuration))).isFalse(); } @Test public void testFilterWithOrShortCircuit2() throws Exception { - Object json = Configuration.defaultConfiguration().jsonProvider().parse("{\"firstname\":\"Bob\",\"surname\":\"Smith\",\"age\":30}"); - assertThat(Filter.parse("[?((@.firstname == 'Bob' || @.firstname == 'Jane') && @.surname == 'Smith')]").apply(createPredicateContext(json))).isTrue(); + Object json = configuration.jsonProvider().parse("{\"firstname\":\"Bob\",\"surname\":\"Smith\",\"age\":30}"); + assertThat(Filter.parse("[?((@.firstname == 'Bob' || @.firstname == 'Jane') && @.surname == 'Smith')]").apply(createPredicateContext(json, configuration))).isTrue(); } @Test diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java index 614f6b6c..73b52217 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java @@ -34,7 +34,7 @@ public class RegexpEvaluatorTest extends BaseTest { @Test public void should_evaluate_regular_expression() { //given - Evaluator evaluator = EvaluatorFactory.createEvaluator(RelationalOperator.REGEX); + Evaluator evaluator = new DefaultEvaluatorFactory().createEvaluator(RelationalOperator.REGEX); ValueNode patternNode = createPatternNode(regexp); Predicate.PredicateContext ctx = createPredicateContext(); diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java index 04ffa00f..0f262ab0 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java @@ -11,6 +11,8 @@ import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.Option; import com.jayway.jsonpath.PathNotFoundException; import com.jayway.jsonpath.internal.Utils; +import com.jayway.jsonpath.internal.filter.DefaultEvaluatorFactory; +import com.jayway.jsonpath.internal.filter.EvaluatorFactory; import com.jayway.jsonpath.spi.cache.LRUCache; import com.jayway.jsonpath.spi.json.GsonJsonProvider; import com.jayway.jsonpath.spi.json.JsonProvider; @@ -837,6 +839,11 @@ public class IssuesTest extends BaseTest { public Configuration configuration() { return Configuration.defaultConfiguration(); } + + @Override + public EvaluatorFactory evaluatorFactory() { + return new DefaultEvaluatorFactory(); + } }; }