Skip to content

Commit

Permalink
Allow a list to be compared to a regex in a filter. (#685)
Browse files Browse the repository at this point in the history
Co-authored-by: Morgan Patch <[email protected]>
  • Loading branch information
MorganEPatch and Morgan Patch authored May 10, 2022
1 parent 2e6b43e commit 3f4900d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.jayway.jsonpath.Predicate;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Pattern;

import static com.jayway.jsonpath.internal.filter.ValueNodes.PatternNode;
import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
Expand Down Expand Up @@ -256,16 +258,41 @@ public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateCont
}

if (left.isPatternNode()) {
return matches(left.asPatternNode(), getInput(right));
if (right.isValueListNode() || (right.isJsonNode() && right.asJsonNode().isArray(ctx))) {
return matchesAny(left.asPatternNode(), right.asJsonNode().asValueListNode(ctx));
} else {
return matches(left.asPatternNode(), getInput(right));
}
} else {
return matches(right.asPatternNode(), getInput(left));
if (left.isValueListNode() || (left.isJsonNode() && left.asJsonNode().isArray(ctx))) {
return matchesAny(right.asPatternNode(), left.asJsonNode().asValueListNode(ctx));
} else {
return matches(right.asPatternNode(), getInput(left));
}
}
}

private boolean matches(PatternNode patternNode, String inputToMatch) {
return patternNode.getCompiledPattern().matcher(inputToMatch).matches();
}

private boolean matchesAny(PatternNode patternNode, ValueNode valueNode) {
if (!valueNode.isValueListNode()) {
return false;
}

ValueListNode listNode = valueNode.asValueListNode();
Pattern pattern = patternNode.getCompiledPattern();

for (Iterator<ValueNode> it = listNode.iterator(); it.hasNext(); ) {
String input = getInput(it.next());
if (pattern.matcher(input).matches()) {
return true;
}
}
return false;
}

private String getInput(ValueNode valueNode) {
String input = "";

Expand Down
14 changes: 13 additions & 1 deletion json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public class FilterTest extends BaseTest {
" \"char-key\" : \"c\", " +
" \"arr-empty\" : [], " +
" \"int-arr\" : [0,1,2,3,4], " +
" \"string-arr\" : [\"a\",\"b\",\"c\",\"d\",\"e\"] " +
" \"string-arr\" : [\"a\",\"b\",\"c\",\"d\",\"e\"], " +
" \"obj\": {\"foo\": \"bar\"}" +
"}"
);

Expand Down Expand Up @@ -263,6 +264,17 @@ public void string_regex_evals() {
assertThat(filter(where("int-key").regex(Pattern.compile("^string$"))).apply(createPredicateContext(json))).isEqualTo(false);
}

@Test
public void list_regex_evals() {
assertThat(filter(where("string-arr").regex(Pattern.compile("^d$"))).apply(createPredicateContext(json))).isEqualTo(true);
assertThat(filter(where("string-arr").regex(Pattern.compile("^q$"))).apply(createPredicateContext(json))).isEqualTo(false);
}

@Test
public void obj_regex_doesnt_break() {
assertThat(filter(where("obj").regex(Pattern.compile("^foo$"))).apply(createPredicateContext(json))).isEqualTo(false);
}

//----------------------------------------------------------------------------
//
// JSON equality
Expand Down
47 changes: 47 additions & 0 deletions json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,47 @@ public class InlineFilterTest extends BaseTest {

private static int bookCount = 4;

public static final String MULTI_STORE_JSON_DOCUMENT = "{\n" +
" \"store\" : [{\n" +
" \"name\": \"First\"," +
" \"book\" : [\n" +
" {\n" +
" \"category\" : \"reference\",\n" +
" \"author\" : \"Nigel Rees\",\n" +
" \"title\" : \"Sayings of the Century\",\n" +
" \"display-price\" : 8.95\n" +
" },\n" +
" {\n" +
" \"category\" : \"fiction\",\n" +
" \"author\" : \"Evelyn Waugh\",\n" +
" \"title\" : \"Sword of Honour\",\n" +
" \"display-price\" : 12.99\n" +
" },\n" +
" {\n" +
" \"category\" : \"fiction\",\n" +
" \"author\" : \"Herman Melville\",\n" +
" \"title\" : \"Moby Dick\",\n" +
" \"isbn\" : \"0-553-21311-3\",\n" +
" \"display-price\" : 8.99\n" +
" },\n" +
" {\n" +
" \"category\" : \"fiction\",\n" +
" \"author\" : \"J. R. R. Tolkien\",\n" +
" \"title\" : \"The Lord of the Rings\",\n" +
" \"isbn\" : \"0-395-19395-8\",\n" +
" \"display-price\" : 22.99\n" +
" }]\n" +
" },\n" +
" {\n" +
" \"name\": \"Second\",\n" +
" \"book\": [\n" +
" {\n" +
" \"category\" : \"fiction\",\n" +
" \"author\" : \"Ernest Hemmingway\",\n" +
" \"title\" : \"The Old Man and the Sea\",\n" +
" \"display-price\" : 12.99\n" +
" }]\n" +
" }]}";

private Configuration conf = Configurations.GSON_CONFIGURATION;

Expand Down Expand Up @@ -125,6 +166,12 @@ public void patterns_can_be_evaluated_with_ignore_case() {
assertThat(resLeft).containsExactly("Nigel Rees");
}

@Test
public void patterns_match_against_lists() {
List<String> haveRefBooks = JsonPath.parse(MULTI_STORE_JSON_DOCUMENT).read("$.store[?(@.book[*].category =~ /Reference/i)].name");
assertThat(haveRefBooks).containsExactly("First");
}

@Test
public void negate_exists_check() {
List<String> hasIsbn = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.isbn)].author");
Expand Down

0 comments on commit 3f4900d

Please sign in to comment.