diff --git a/changelog.md b/changelog.md index 0158c619..06cef739 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,8 @@ In The Pipe * 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")` +* Negate exist checks in inline filters (not defined or null) + `parse(JSON_DOCUMENT).read("$.store.book[?(!@.isbn)]")` 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 c13d6391..f5091ba6 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java @@ -360,8 +360,13 @@ public class Criteria implements Predicate { boolean exists = ((Boolean) right); try { Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build(); - ((Path)left).evaluate(ctx.item(), ctx.root(), c).getValue(); - return exists; + Object value = ((Path) left).evaluate(ctx.item(), ctx.root(), c).getValue(); + if(exists){ + return (value != null); + } else { + return (value == null); + } + } catch (PathNotFoundException e) { return !exists; } @@ -654,7 +659,8 @@ public class Criteria implements Predicate { } private static boolean isPath(String string){ - return (string != null && (string.startsWith("$") || string.startsWith("@"))); + return (string != null + && (string.startsWith("$") || string.startsWith("@") || string.startsWith("!@"))); } private static boolean isString(String string){ @@ -717,8 +723,13 @@ public class Criteria implements Predicate { Object rightPrepared = right; Path leftPath = null; Path rightPath = null; + boolean existsCheck = true; if(isPath(left)){ + if(left.charAt(0) == '!'){ + existsCheck = false; + left = left.substring(1); + } leftPath = PathCompiler.compile(left); if(!leftPath.isDefinite()){ throw new InvalidPathException("the predicate path: " + left + " is not definite"); @@ -731,6 +742,9 @@ public class Criteria implements Predicate { } if(isPath(right)){ + if(right.charAt(0) == '!'){ + throw new InvalidPathException("Invalid negation! Can only be used for existence check e.g [?(!@.foo)]"); + } rightPath = PathCompiler.compile(right); if(!rightPath.isDefinite()){ throw new InvalidPathException("the predicate path: " + right + " is not definite"); @@ -743,7 +757,7 @@ public class Criteria implements Predicate { } if(leftPath != null && operator.isEmpty()){ - return Criteria.where(leftPath).exists(true); + return Criteria.where(leftPath).exists(existsCheck); } else { return new Criteria(leftPrepared, CriteriaType.parse(operator), rightPrepared); } 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 8f284204..2f8cd215 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java @@ -2,6 +2,7 @@ package com.jayway.jsonpath; import org.junit.Test; +import java.util.ArrayList; import java.util.List; import static java.util.Arrays.asList; @@ -129,4 +130,31 @@ public class InlineFilterTest extends BaseTest { resLeft = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /REFERENCE/i)].author"); assertThat(resLeft).containsExactly("Nigel Rees"); } + + @Test + public void negate_exists_check() { + List hasIsbn = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.isbn)].author"); + assertThat(hasIsbn).containsExactly("Herman Melville", "J. R. R. Tolkien"); + + List noIsbn = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(!@.isbn)].author"); + + assertThat(noIsbn).containsExactly("Nigel Rees", "Evelyn Waugh"); + } + + @Test + public void negate_exists_check_primitive() { + List ints = new ArrayList(); + ints.add(0); + ints.add(1); + ints.add(null); + ints.add(2); + ints.add(3); + + + List notNull = JsonPath.parse(ints).read("$[?(@)]"); + assertThat(notNull).containsExactly(0,1,2,3); + + List isNull = JsonPath.parse(ints).read("$[?(!@)]"); + assertThat(isNull).containsExactly(new Integer[]{null}); + } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java index 66a43f4f..f5ab41da 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java @@ -191,7 +191,6 @@ public class FilterTest extends BaseTest { } @Test - //@Ignore //TODO: finalize behaviour public void exists_filters_evaluates() throws Exception { Map check = new HashMap(); check.put("foo", "foo"); @@ -200,8 +199,8 @@ public class FilterTest extends BaseTest { assertTrue(filter(where("foo").exists(true)).apply(createPredicateContext(check))); assertFalse(filter(where("foo").exists(false)).apply(createPredicateContext(check))); - assertTrue(filter(where("foo_null").exists(true)).apply(createPredicateContext(check))); - assertFalse(filter(where("foo_null").exists(false)).apply(createPredicateContext(check))); + assertTrue(filter(where("foo_null").exists(false)).apply(createPredicateContext(check))); + assertFalse(filter(where("foo_null").exists(true)).apply(createPredicateContext(check))); assertTrue(filter(where("bar").exists(false)).apply(createPredicateContext(check))); assertFalse(filter(where("bar").exists(true)).apply(createPredicateContext(check)));