Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Filters are logical expressions used to filter arrays. A typical filter would be
| =~ | 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 |

Expand Down
27 changes: 27 additions & 0 deletions json-path/src/main/java/com/jayway/jsonpath/Criteria.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,33 @@ public Criteria nin(Collection<?> c) {
return this;
}

/**
* The <code>subsetof</code> operator selects objects for which the specified field is
* an array whose elements comprise a subset of the set comprised by the elements of
* the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria subsetof(Object... o) {
return subsetof(Arrays.asList(o));
}

/**
* The <code>subsetof</code> operator selects objects for which the specified field is
* an array whose elements comprise a subset of the set comprised by the elements of
* the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria subsetof(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.SUBSETOF;
this.right = new ValueNode.ValueListNode(c);
return this;
}

/**
* The <code>all</code> operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class EvaluatorFactory {
evaluators.put(RelationalOperator.CONTAINS, new ContainsEvaluator());
evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator());
evaluators.put(RelationalOperator.TYPE, new TypeEvaluator());
evaluators.put(RelationalOperator.SUBSETOF, new SubsetOfEvaluator());
}

public static Evaluator createEvaluator(RelationalOperator operator){
Expand Down Expand Up @@ -264,4 +265,34 @@ private String getInput(ValueNode valueNode) {
return input;
}
}

private static class SubsetOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueNode.ValueListNode rightValueListNode;
if(right.isJsonNode()){
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
return false;
} else {
rightValueListNode = vn.asValueListNode();
}
} else {
rightValueListNode = right.asValueListNode();
}
ValueNode.ValueListNode leftValueListNode;
if(left.isJsonNode()){
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
return false;
} else {
leftValueListNode = vn.asValueListNode();
}
} else {
leftValueListNode = left.asValueListNode();
}
return leftValueListNode.subsetof(rightValueListNode);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public enum RelationalOperator {
EXISTS("EXISTS"),
TYPE("TYPE"),
MATCHES("MATCHES"),
EMPTY("EMPTY");
EMPTY("EMPTY"),
SUBSETOF("SUBSETOF");

private final String operatorString;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,15 @@ public boolean contains(ValueNode node){
return nodes.contains(node);
}

public boolean subsetof(ValueListNode right) {
for (ValueNode leftNode : nodes) {
if (!right.nodes.contains(leftNode)) {
return false;
}
}
return true;
}

public List<ValueNode> getNodes() {
return Collections.unmodifiableList(nodes);
}
Expand Down
10 changes: 10 additions & 0 deletions json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jayway.jsonpath;

import java.util.Collections;
import org.assertj.core.api.Assertions;
import org.junit.Test;

Expand Down Expand Up @@ -142,6 +143,15 @@ public void a_size_filter_can_be_serialized() {
assertThat(filter).isEqualTo(parsed);
}

@Test
public void a_subsetof_filter_can_be_serialized() {

String filter = filter(where("a").subsetof(Collections.emptyList())).toString();
String parsed = parse("[?(@['a'] SUBSETOF [])]").toString();

assertThat(filter).isEqualTo(parsed);
}

@Test
public void a_exists_filter_can_be_serialized() {

Expand Down
20 changes: 20 additions & 0 deletions json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.jayway.jsonpath;

import java.util.ArrayList;
import org.assertj.core.util.Lists;
import org.junit.Test;

import java.util.HashMap;
Expand Down Expand Up @@ -358,6 +360,24 @@ public void null_size_evals() {
assertThat(filter(where("null-key").size(6)).apply(createPredicateContext(json))).isEqualTo(false);
}

//----------------------------------------------------------------------------
//
// SUBSETOF
//
//----------------------------------------------------------------------------
@Test
public void array_subsetof_evals() {
// list is a superset
List<String> list = Lists.newArrayList("a", "b", "c", "d", "e", "f", "g");
assertThat(filter(where("string-arr").subsetof(list)).apply(createPredicateContext(json))).isEqualTo(true);
// list is exactly the same set (but in a different order)
list = Lists.newArrayList("e", "d", "b", "c", "a");
assertThat(filter(where("string-arr").subsetof(list)).apply(createPredicateContext(json))).isEqualTo(true);
// list is missing one element
list = Lists.newArrayList("a", "b", "c", "d");
assertThat(filter(where("string-arr").subsetof(list)).apply(createPredicateContext(json))).isEqualTo(false);
}

//----------------------------------------------------------------------------
//
// EXISTS
Expand Down