From 78c8d908c1c8342700c70ee28fbeb0843830bf3e Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Sun, 26 Oct 2014 17:17:15 +0100 Subject: [PATCH] Support for regex in inline filters. --- changelog.md | 6 +++ .../java/com/jayway/jsonpath/Criteria.java | 49 +++++++++++++++++-- .../com/jayway/jsonpath/InlineFilterTest.java | 20 ++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 6c0bb579..0158c619 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,12 @@ In The Pipe `parse(JSON_DOCUMENT).put("$.store.book[1]", "new-key", "new-val")` `parse(JSON_DOCUMENT).add("$.store.book", newBook)` `parse(JSON_DOCUMENT).delete("$.store.book[1].display-price")` +* Support regex in inline filters (ruby syntax) + `parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /reference/)].author")` + `parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /REFERENCE/i)].author")` +* Inline filter does not force path first + `parse(JSON_DOCUMENT).read("$.store.book[?(@.category == 'reference')].author")` + `parse(JSON_DOCUMENT).read("$.store.book[?('reference' == @.category)].author")` 1.1.0 (2014-10-01) diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java index 0c992d5b..5234c3a1 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java @@ -45,7 +45,8 @@ public class Criteria implements Predicate { CriteriaType.LTE.toString(), CriteriaType.NE.toString(), CriteriaType.LT.toString(), - CriteriaType.GT.toString() + CriteriaType.GT.toString(), + CriteriaType.REGEX.toString() }; private Object left; @@ -232,13 +233,27 @@ public class Criteria implements Predicate { @Override boolean eval(Object left, Object right, PredicateContext ctx) { boolean res = false; - final Pattern pattern = (Pattern) left; - if (right != null && right instanceof String) { - res = pattern.matcher(right.toString()).matches(); + Pattern pattern; + Object target; + + if(right instanceof Pattern){ + pattern = (Pattern) right; + target = left; + } else { + pattern = (Pattern) left; + target = right; + } + + if(target != null){ + res = pattern.matcher(target.toString()).matches(); } - if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left.toString(), res); + if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right.toString(), name(), left.toString(), res); return res; } + @Override + public String toString() { + return "=~"; + } }, MATCHES { @Override @@ -282,6 +297,8 @@ public class Criteria implements Predicate { return LTE; } else if ("!=".equals(str)) { return NE; + } else if ("=~".equals(str)) { + return REGEX; } else { throw new UnsupportedOperationException("CriteriaType " + str + " can not be parsed"); } @@ -639,9 +656,27 @@ public class Criteria implements Predicate { private static boolean isPath(String string){ return (string != null && (string.startsWith("$") || string.startsWith("@"))); } + private static boolean isString(String string){ return (string != null && !string.isEmpty() && string.charAt(0) == '\'' && string.charAt(string.length() - 1) == '\''); } + private static boolean isPattern(String string){ + return (string != null + && !string.isEmpty() + && string.charAt(0) == '/' + && (string.charAt(string.length() - 1) == '/' || (string.charAt(string.length() - 2) == '/' && string.charAt(string.length() - 1) == 'i')) + ); + } + + private static Pattern compilePattern(String string) { + int lastIndex = string.lastIndexOf('/'); + boolean ignoreCase = string.endsWith("i"); + String regex = string.substring(1, lastIndex); + + int flags = ignoreCase ? Pattern.CASE_INSENSITIVE : 0; + return Pattern.compile(regex, flags); + } + /** @@ -691,6 +726,8 @@ public class Criteria implements Predicate { leftPrepared = leftPath; } else if(isString(left)) { leftPrepared = left.substring(1, left.length() - 1); + } else if(isPattern(left)){ + leftPrepared = compilePattern(left); } if(isPath(right)){ @@ -701,6 +738,8 @@ public class Criteria implements Predicate { rightPrepared = rightPath; } else if(isString(right)) { rightPrepared = right.substring(1, right.length() - 1); + } else if(isPattern(right)){ + rightPrepared = compilePattern(right); } if(leftPath != null && operator.isEmpty()){ diff --git a/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java index 3a38610e..8f284204 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java @@ -109,4 +109,24 @@ public class InlineFilterTest extends BaseTest { assertThat(res).containsExactly("Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"); } + + @Test + public void patterns_can_be_evaluated() { + List resLeft = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /reference/)].author"); + assertThat(resLeft).containsExactly("Nigel Rees"); + + resLeft = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(/reference/ =~ @.category)].author"); + assertThat(resLeft).containsExactly("Nigel Rees"); + } + + + + @Test + public void patterns_can_be_evaluated_with_ignore_case() { + List resLeft = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /REFERENCE/)].author"); + assertThat(resLeft).isEmpty(); + + resLeft = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /REFERENCE/i)].author"); + assertThat(resLeft).containsExactly("Nigel Rees"); + } }