diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java index c4030bc7..2a64a6f2 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java @@ -23,6 +23,7 @@ import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.minidev.json.JSONArray; /** * Moved these nodes out of the ValueNode abstract class. @@ -626,7 +627,11 @@ public interface ValueNodes { try { Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build(); Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false); - return result == JsonProvider.UNDEFINED ? FALSE : TRUE; + if (((PredicateContextImpl)ctx).isExtractingParentNode() && result instanceof JSONArray) { + return (result == JsonProvider.UNDEFINED) || (((JSONArray) result).size() == 0) ? FALSE : TRUE; + } else { + return result == JsonProvider.UNDEFINED ? FALSE : TRUE; + } } catch (PathNotFoundException e) { return FALSE; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java index 80ac2bd4..ed8030b5 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java @@ -94,6 +94,11 @@ public class CompiledPath implements Path { } EvaluationContextImpl ctx = new EvaluationContextImpl(this, rootDocument, configuration, forUpdate); + + if (document == rootDocument){ + ctx.setRoot(); + } + try { PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP; root.evaluate("", op, document, ctx); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java index 3fc7e487..c2c894ed 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java @@ -49,8 +49,17 @@ public class EvaluationContextImpl implements EvaluationContext { private final HashMap documentEvalCache = new HashMap(); private final boolean forUpdate; private int resultIndex = 0; + private boolean root = false; + public boolean isRoot() { + return root; + } + + public void setRoot() { + root = true; + } + public EvaluationContextImpl(Path path, Object rootDocument, Configuration configuration, boolean forUpdate) { notNull(path, "path can not be null"); notNull(rootDocument, "root can not be null"); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java index d471b6ce..adb4d8fb 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java @@ -31,6 +31,7 @@ public class PredicateContextImpl implements Predicate.PredicateContext { private final Object rootDocument; private final Configuration configuration; private final HashMap documentPathCache; + private boolean isExtractingParentNode = false; public PredicateContextImpl(Object contextDocument, Object rootDocument, Configuration configuration, HashMap documentPathCache) { this.contextDocument = contextDocument; @@ -39,6 +40,14 @@ public class PredicateContextImpl implements Predicate.PredicateContext { this.documentPathCache = documentPathCache; } + public boolean isExtractingParentNode(){ + return isExtractingParentNode; + } + + public void setExtractingParentNode(){ + this.isExtractingParentNode = true; + } + public Object evaluate(Path path){ Object result; if(path.isRootPath()){ diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java index ee67fc1e..ee31cf2a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java @@ -71,7 +71,9 @@ public class PredicatePathToken extends PathToken { public boolean accept(final Object obj, final Object root, final Configuration configuration, EvaluationContextImpl evaluationContext) { Predicate.PredicateContext ctx = new PredicateContextImpl(obj, root, configuration, evaluationContext.documentEvalCache()); - + if (evaluationContext.isRoot()){ + ((PredicateContextImpl) ctx).setExtractingParentNode(); + } for (Predicate predicate : predicates) { try { if (!predicate.apply(ctx)) { diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue394.java b/json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue394.java new file mode 100644 index 00000000..91cf7717 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue394.java @@ -0,0 +1,38 @@ +package com.jayway.jsonpath.internal.function; + +import net.minidev.json.JSONArray; +import com.jayway.jsonpath.JsonPath; +import org.junit.Test; +import java.io.InputStream; +import java.util.LinkedHashMap; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class Issue394 { + + @Test + public void testGetParentNodeByChildNodeFromStream(){ + InputStream stream = ClassLoader.getSystemResourceAsStream("issue_394.json"); + + JSONArray arr = JsonPath.parse(stream).read("$.street.store.book[?(@.authors[?(@.lastName == 'Waugh')])]"); + + assertEquals(arr.size(),1); + } + + @Test + public void testGetParentNodeByChildNodeFromJson(){ + String json = "{\n" + + " \"parent\": {\n" + + " \"children\": [\n{\n" + + " \"index\": \"0\",\n" + + " \"name\": \"A\"\n},\n{\n" + + " \"index\": \"1\",\n" + + " \"name\": \"B\"\n},\n{\n" + + " \"index\": \"2\",\n" + + " \"name\": \"C\"\n}\n]\n}\n}"; + + JSONArray arr = JsonPath.read(json,"$.parent[?(@.children[?(@.index == '2')])]"); + + assertNotNull(((LinkedHashMap) arr.get(0)).get("children")); + } +} diff --git a/json-path/src/test/resources/issue_394.json b/json-path/src/test/resources/issue_394.json new file mode 100644 index 00000000..8fe9beff --- /dev/null +++ b/json-path/src/test/resources/issue_394.json @@ -0,0 +1,75 @@ +{ + "street": { + "store": { + "book": [ + { + "category": "reference", + "authors": [ + { + "firstName": "Nigel", + "lastName": "Rees" + }, + { + "firstName": "Evelyn", + "lastName": "Waugh" + } + ], + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "authors": [ + { + "firstName": "A", + "lastName": "B" + }, + { + "firstName": "C", + "lastName": "D" + } + ], + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "authors": [ + { + "firstName": "A", + "lastName": "D" + }, + { + "firstName": "Evelyn", + "lastName": "X" + } + ], + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "authors": [ + { + "firstName": "Nigel", + "lastName": "Rees" + }, + { + "firstName": "Evelyn", + "lastName": "X" + } + ], + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + }, + "expensive": 10 + } +} \ No newline at end of file