diff --git a/README.md b/README.md
index 90826ee2..1081c580 100644
--- a/README.md
+++ b/README.md
@@ -77,13 +77,13 @@ Functions
Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression.
The function output is dictated by the function itself.
-| Function | Description | Output |
-| :------------------------ | :----------------------------------------------------------------- |-----------|
-| min() | Provides the min value of an array of numbers | Double |
-| max() | Provides the max value of an array of numbers | Double |
-| avg() | Provides the average value of an array of numbers | Double |
-| stddev() | Provides the standard deviation value of an array of numbers | Double |
-| length() | Provides the length of an array | Integer |
+| Function | Description | Output |
+| :------------------------ | :------------------------------------------------------------------ |-----------|
+| min() | Provides the min value of an array of numbers | Double |
+| max() | Provides the max value of an array of numbers | Double |
+| avg() | Provides the average value of an array of numbers | Double |
+| stddev() | Provides the standard deviation value of an array of numbers | Double |
+| length() | Provides the length of an array | Integer |
Filter Operators
@@ -91,20 +91,22 @@ Filter Operators
Filters are logical expressions used to filter arrays. A typical filter would be `[?(@.age > 18)]` where `@` represents the current item being processed. More complex filters can be created with logical operators `&&` and `||`. String literals must be enclosed by single or double quotes (`[?(@.color == 'blue')]` or `[?(@.color == "blue")]`).
-| Operator | Description |
-| :----------------------- | :---------------------------------------------------------------- |
-| == | left is equal to right (note that 1 is not equal to '1') |
-| != | left is not equal to right |
-| < | left is less than right |
-| <= | left is less or equal to right |
-| > | left is greater than right |
-| >= | left is greater than or equal to right |
-| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] |
-| in | left exists in right [?(@.size in ['S', 'M'])] |
-| nin | left does not exists in right |
-| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
-| size | size of left (array or string) should match right |
-| empty | left (array or string) should be empty |
+| Operator | Description |
+| :----------------------- | :-------------------------------------------------------------------- |
+| == | left is equal to right (note that 1 is not equal to '1') |
+| != | left is not equal to right |
+| < | left is less than right |
+| <= | left is less or equal to right |
+| > | left is greater than right |
+| >= | left is greater than or equal to right |
+| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] |
+| in | left exists in right [?(@.size in ['S', 'M'])] |
+| nin | left does not exists in right |
+| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
+| anyof | left has an intersection with right [?(@.sizes anyof ['M', 'L'])] |
+| noneof | left has no intersection with right [?(@.sizes noneof ['M', 'L'])] |
+| size | size of left (array or string) should match right |
+| empty | left (array or string) should be empty |
Path Examples
diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java
old mode 100644
new mode 100755
index ea32460e..7f2706df
--- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java
@@ -19,6 +19,7 @@ import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.filter.RelationalExpressionNode;
import com.jayway.jsonpath.internal.filter.RelationalOperator;
import com.jayway.jsonpath.internal.filter.ValueNode;
+import com.jayway.jsonpath.internal.filter.ValueNodes;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,9 +29,8 @@ import java.util.List;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.Utils.notNull;
-import com.jayway.jsonpath.internal.filter.ValueNodes;
-import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
import static com.jayway.jsonpath.internal.filter.ValueNodes.PredicateNode;
+import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
/**
*
@@ -296,6 +296,56 @@ public class Criteria implements Predicate {
return this;
}
+ /**
+ * The anyof
operator selects objects for which the specified field is
+ * an array that contain at least an element in the specified array.
+ *
+ * @param o the values to match against
+ * @return the criteria
+ */
+ public Criteria anyof(Object... o) {
+ return subsetof(Arrays.asList(o));
+ }
+
+ /**
+ * The anyof
operator selects objects for which the specified field is
+ * an array that contain at least an element in the specified array.
+ *
+ * @param c the values to match against
+ * @return the criteria
+ */
+ public Criteria anyof(Collection> c) {
+ notNull(c, "collection can not be null");
+ this.criteriaType = RelationalOperator.ANYOF;
+ this.right = new ValueListNode(c);
+ return this;
+ }
+
+ /**
+ * The noneof
operator selects objects for which the specified field is
+ * an array that does not contain any of the elements of the specified array.
+ *
+ * @param o the values to match against
+ * @return the criteria
+ */
+ public Criteria noneof(Object... o) {
+ return subsetof(Arrays.asList(o));
+ }
+
+ /**
+ * The noneof
operator selects objects for which the specified field is
+ * an array that does not contain any of the elements of the specified array.
+ *
+ * @param c the values to match against
+ * @return the criteria
+ */
+ public Criteria noneof(Collection> c) {
+ notNull(c, "collection can not be null");
+ this.criteriaType = RelationalOperator.NONEOF;
+ this.right = new ValueListNode(c);
+ return this;
+ }
+
/**
* The all
operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
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
old mode 100644
new mode 100755
index 0669e7e8..cfbfdff7
--- 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
@@ -2,11 +2,13 @@ package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Predicate;
-import static com.jayway.jsonpath.internal.filter.ValueNodes.*;
import java.util.HashMap;
import java.util.Map;
+import static com.jayway.jsonpath.internal.filter.ValueNodes.PatternNode;
+import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
+
public class EvaluatorFactory {
private static final Map evaluators = new HashMap();
@@ -31,6 +33,8 @@ public class EvaluatorFactory {
evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator());
evaluators.put(RelationalOperator.TYPE, new TypeEvaluator());
evaluators.put(RelationalOperator.SUBSETOF, new SubsetOfEvaluator());
+ evaluators.put(RelationalOperator.ANYOF, new AnyOfEvaluator());
+ evaluators.put(RelationalOperator.NONEOF, new NoneOfEvaluator());
}
public static Evaluator createEvaluator(RelationalOperator operator){
@@ -296,4 +300,77 @@ public class EvaluatorFactory {
}
}
+ private static class AnyOfEvaluator implements Evaluator {
+ @Override
+ public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
+ ValueListNode rightValueListNode;
+ if (right.isJsonNode()) {
+ ValueNode vn = right.asJsonNode().asValueListNode(ctx);
+ if (vn.isUndefinedNode()) {
+ return false;
+ } else {
+ rightValueListNode = vn.asValueListNode();
+ }
+ } else {
+ rightValueListNode = right.asValueListNode();
+ }
+ ValueListNode leftValueListNode;
+ if (left.isJsonNode()) {
+ ValueNode vn = left.asJsonNode().asValueListNode(ctx);
+ if (vn.isUndefinedNode()) {
+ return false;
+ } else {
+ leftValueListNode = vn.asValueListNode();
+ }
+ } else {
+ leftValueListNode = left.asValueListNode();
+ }
+
+ for (ValueNode leftValueNode : leftValueListNode) {
+ for (ValueNode rightValueNode : rightValueListNode) {
+ if (leftValueNode.equals(rightValueNode)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ private static class NoneOfEvaluator implements Evaluator {
+ @Override
+ public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
+ ValueListNode rightValueListNode;
+ if (right.isJsonNode()) {
+ ValueNode vn = right.asJsonNode().asValueListNode(ctx);
+ if (vn.isUndefinedNode()) {
+ return false;
+ } else {
+ rightValueListNode = vn.asValueListNode();
+ }
+ } else {
+ rightValueListNode = right.asValueListNode();
+ }
+ ValueListNode leftValueListNode;
+ if (left.isJsonNode()) {
+ ValueNode vn = left.asJsonNode().asValueListNode(ctx);
+ if (vn.isUndefinedNode()) {
+ return false;
+ } else {
+ leftValueListNode = vn.asValueListNode();
+ }
+ } else {
+ leftValueListNode = left.asValueListNode();
+ }
+
+ for (ValueNode leftValueNode : leftValueListNode) {
+ for (ValueNode rightValueNode : rightValueListNode) {
+ if (leftValueNode.equals(rightValueNode)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
}
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 830cc3bb..84e2aed0 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
@@ -30,7 +30,9 @@ public enum RelationalOperator {
TYPE("TYPE"),
MATCHES("MATCHES"),
EMPTY("EMPTY"),
- SUBSETOF("SUBSETOF");
+ SUBSETOF("SUBSETOF"),
+ ANYOF("ANYOF"),
+ NONEOF("NONEOF");
private final String operatorString;
diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java
index 948ca75f..37e37f79 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java
@@ -1,9 +1,9 @@
package com.jayway.jsonpath;
-import java.util.Collections;
import org.assertj.core.api.Assertions;
import org.junit.Test;
+import java.util.Collections;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria.where;
@@ -152,6 +152,24 @@ public class FilterParseTest {
assertThat(filter).isEqualTo(parsed);
}
+ @Test
+ public void a_anyof_filter_can_be_serialized() {
+
+ String filter = filter(where("a").anyof(Collections.emptyList())).toString();
+ String parsed = parse("[?(@['a'] ANYOF [])]").toString();
+
+ assertThat(filter).isEqualTo(parsed);
+ }
+
+ @Test
+ public void a_noneof_filter_can_be_serialized() {
+
+ String filter = filter(where("a").noneof(Collections.emptyList())).toString();
+ String parsed = parse("[?(@['a'] NONEOF [])]").toString();
+
+ assertThat(filter).isEqualTo(parsed);
+ }
+
@Test
public void a_exists_filter_can_be_serialized() {
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 366a8db9..545f29c6 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
@@ -1,6 +1,5 @@
package com.jayway.jsonpath;
-import java.util.ArrayList;
import org.assertj.core.util.Lists;
import org.junit.Test;
@@ -379,6 +378,36 @@ public class FilterTest extends BaseTest {
assertThat(filter(where("string-arr").subsetof(list)).apply(createPredicateContext(json))).isEqualTo(false);
}
+ //----------------------------------------------------------------------------
+ //
+ // ANYOF
+ //
+ //----------------------------------------------------------------------------
+ @Test
+ public void array_anyof_evals() {
+ List list = Lists.newArrayList("a", "z");
+ assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(true);
+ list = Lists.newArrayList("z", "b", "a");
+ assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(true);
+ list = Lists.newArrayList("x", "y", "z");
+ assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(false);
+ }
+
+ //----------------------------------------------------------------------------
+ //
+ // NONEOF
+ //
+ //----------------------------------------------------------------------------
+ @Test
+ public void array_noneof_evals() {
+ List list = Lists.newArrayList("a", "z");
+ assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(false);
+ list = Lists.newArrayList("z", "b", "a");
+ assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(false);
+ list = Lists.newArrayList("x", "y", "z");
+ assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(true);
+ }
+
//----------------------------------------------------------------------------
//
// EXISTS