diff --git a/README b/README index f7853d9b..88301efb 100644 --- a/README +++ b/README @@ -1,5 +1,4 @@ Java DSL for reading and testing JSON documents. - - -Santa Fe LET 50 \ No newline at end of file +COPY DEPENDENCIES : mvn clean compile dependency:copy-dependencies -DstripVersion +FONT LOGO : Santa Fe LET 50 \ No newline at end of file diff --git a/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java b/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java index 2087653c..6e14afec 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java +++ b/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java @@ -177,7 +177,7 @@ public class JsonPath { } - private static Object parse(String json) throws java.text.ParseException { + public static Object parse(String json) throws java.text.ParseException { try { return JSON_PARSER.parse(json); } catch (ParseException e) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/PathUtil.java b/json-path/src/main/java/com/jayway/jsonpath/PathUtil.java index f451a52a..ee8c9727 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/PathUtil.java +++ b/json-path/src/main/java/com/jayway/jsonpath/PathUtil.java @@ -104,17 +104,16 @@ public class PathUtil { sb.append(c); } } - if (includeSopChar) { assertValidPeek(pathQueue, false, stopChars); sb.append(pathQueue.poll()); } else { assertValidPeek(pathQueue, true, stopChars); } - return unWrapProperty(sb); + return clean(sb); } - private static String unWrapProperty(StringBuilder sb) { + private static String clean(StringBuilder sb) { String src = sb.toString(); diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/PathToken.java b/json-path/src/main/java/com/jayway/jsonpath/reader/PathToken.java new file mode 100644 index 00000000..fa44e7a6 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/PathToken.java @@ -0,0 +1,23 @@ +package com.jayway.jsonpath.reader; + +import com.jayway.jsonpath.reader.filter.Filter; +import com.jayway.jsonpath.reader.filter.FilterFactory; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 10:00 PM + */ +public class PathToken { + + private Filter filter; + + public PathToken(String pathFragment) { + filter = FilterFactory.createFilter(pathFragment); + } + + public Object filter(Object model){ + return filter.filter(model); + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/PathTokenizer.java b/json-path/src/main/java/com/jayway/jsonpath/reader/PathTokenizer.java new file mode 100644 index 00000000..e3f97c07 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/PathTokenizer.java @@ -0,0 +1,210 @@ +package com.jayway.jsonpath.reader; + +import com.jayway.jsonpath.InvalidPathException; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 9:53 PM + */ +public class PathTokenizer implements Iterable { + + private char[] pathChars; + private int index = 0; + private List pathTokens = new LinkedList(); + + public PathTokenizer(String jsonPath) { + + if (!jsonPath.startsWith("$") && !jsonPath.startsWith("$[")) { + jsonPath = "$." + jsonPath; + } + pathChars = jsonPath.toCharArray(); + + for (String pathFragment : splitPath()) { + pathTokens.add(new PathToken(pathFragment)); + } + } + + public Iterator iterator() { + return pathTokens.iterator(); + } + + + //-------------------------------------------- + // + // Split path + // + //-------------------------------------------- + private boolean isEmpty() { + return index == pathChars.length; + } + + private char peek() { + return pathChars[index]; + } + + private char poll() { + char peek = peek(); + index++; + return peek; + } + + public List splitPath() { + + List fragments = new LinkedList(); + while (!isEmpty()) { + skip(' '); + char current = peek(); + + switch (current) { + case '$': + fragments.add(Character.toString(current)); + poll(); + break; + + case '.': + poll(); + if (peek() == '.') { + poll(); + fragments.add(".."); + + assertNotInvalidPeek('.'); + } + break; + + case '[': + fragments.add(extract(true, ']')); + break; + + default: + fragments.add(extract(false, '[', '.')); + + } + } + return fragments; + } + + + private String extract(boolean includeSopChar, char... stopChars) { + StringBuilder sb = new StringBuilder(); + while (!isEmpty() && (!isStopChar(peek(), stopChars))) { + + char c = poll(); + + if (isStopChar(c, stopChars)) { + if (includeSopChar) { + sb.append(c); + } + } else { + sb.append(c); + } + } + if (includeSopChar) { + assertValidPeek(false, stopChars); + sb.append(poll()); + } else { + assertValidPeek(true, stopChars); + } + return clean(sb); + } + + private String clean(StringBuilder sb) { + + String src = sb.toString(); + + src = trim(src, "'"); + src = trim(src, ")"); + src = trim(src, "("); + src = trimLeft(src, "?"); + src = trimLeft(src, "@"); + + if (src.length() > 5 && src.subSequence(0, 2).equals("['")) { + src = src.substring(2); + src = src.substring(0, src.length() - 2); + } + + return src.trim(); + } + + private String trim(String src, String trim) { + return trimLeft(trimRight(src, trim), trim); + } + + private String trimRight(String src, String trim) { + String scanFor = trim + " "; + if (src.contains(scanFor)) { + while (src.contains(scanFor)) { + src = src.replace(scanFor, trim); + } + } + return src; + } + + private String trimLeft(String src, String trim) { + String scanFor = " " + trim; + if (src.contains(scanFor)) { + while (src.contains(scanFor)) { + src = src.replace(scanFor, trim); + } + } + return src; + } + + private boolean isStopChar(char c, char... scanFor) { + boolean found = false; + for (char check : scanFor) { + if (check == c) { + found = true; + break; + } + } + return found; + } + + private void skip(char target) { + if (isEmpty()) { + return; + } + while (pathChars[index] == target) { + index++; + } + } + + private void assertNotInvalidPeek(char... invalidChars) { + if (isEmpty()) { + return; + } + char peek = peek(); + for (char check : invalidChars) { + if (check == peek) { + throw new InvalidPathException("Char: " + peek + " at current position is not valid!"); + } + } + } + + private void assertValidPeek(boolean acceptEmpty, char... validChars) { + if (isEmpty() && acceptEmpty) { + return; + } + if (isEmpty()) { + throw new InvalidPathException("Path is incomplete"); + } + boolean found = false; + char peek = peek(); + for (char check : validChars) { + if (check == peek) { + found = true; + break; + } + } + if (!found) { + throw new InvalidPathException("Path is invalid"); + } + } + +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ArrayEvalFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ArrayEvalFilter.java new file mode 100644 index 00000000..a6da0b48 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ArrayEvalFilter.java @@ -0,0 +1,100 @@ +package com.jayway.jsonpath.reader.filter; + +import com.jayway.jsonpath.InvalidPathException; +import com.jayway.jsonpath.eval.ExpressionEvaluator; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/5/11 + * Time: 12:35 AM + */ +public class ArrayEvalFilter extends Filter { + + public static final Pattern PATTERN = Pattern.compile("(.*?)\\s?([=<>]+)\\s?(.*)"); + + public ArrayEvalFilter(String condition) { + super(condition); + } + + @Override + public Object filter(Object obj) { + //[?(@.isbn = 10)] + List src = toList(obj); + List result = new LinkedList(); + + String trimmedCondition = trim(condition, 5, 2); + + ConditionStatement conditionStatement = createConditionStatement(trimmedCondition); + + + for (Object item : src) { + if (isMatch(item, conditionStatement)) { + result.add(item); + } + } + return result; + } + + private boolean isMatch(Object check, ConditionStatement conditionStatement) { + if (!isMap(check)) { + return false; + } + Map obj = toMap(check); + + if (!obj.containsKey(conditionStatement.getField())) { + return false; + } + + Object propertyValue = obj.get(conditionStatement.getField()); + + if (isContainer(propertyValue)) { + return false; + } + return ExpressionEvaluator.eval(propertyValue, conditionStatement.getOperator(), conditionStatement.getExpected()); + } + + + private ConditionStatement createConditionStatement(String str) { + Matcher matcher = PATTERN.matcher(str); + if (matcher.matches()) { + String property = matcher.group(1); + String operator = matcher.group(2); + String expected = matcher.group(3); + + return new ConditionStatement(property, operator, expected); + } else { + throw new InvalidPathException("Invalid match " + str); + } + } + + private class ConditionStatement { + private String field; + private String operator; + private String expected; + + private ConditionStatement(String field, String operator, String expected) { + this.field = field; + this.operator = operator; + this.expected = expected; + } + + public String getField() { + return field; + } + + public String getOperator() { + return operator; + } + + public String getExpected() { + return expected; + } + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ArrayIndexFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ArrayIndexFilter.java new file mode 100644 index 00000000..a51beec5 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ArrayIndexFilter.java @@ -0,0 +1,52 @@ +package com.jayway.jsonpath.reader.filter; + +import java.util.LinkedList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 11:25 PM + */ +public class ArrayIndexFilter extends Filter { + public ArrayIndexFilter(String condition) { + super(condition); + } + + @Override + public Object filter(Object obj) { + + List src = toList(obj); + List result = new LinkedList(); + + String trimmedCondition = trim(condition, 1, 1); + + if (trimmedCondition.startsWith(":")) { + trimmedCondition = trim(trimmedCondition, 1, 0); + int get = Integer.parseInt(trimmedCondition); + for (int i = 0; i < get; i++) { + result.add(src.get(i)); + } + return result; + + } else if (trimmedCondition.endsWith(":")) { + trimmedCondition = trim(trimmedCondition, 1, 1); + int get = Integer.parseInt(trimmedCondition); + return src.get(src.size() - get); + } else { + String[] indexArr = trimmedCondition.split(","); + + if (indexArr.length == 1) { + + return src.get(Integer.parseInt(indexArr[0])); + + } else { + for (String idx : indexArr) { + result.add(src.get(Integer.parseInt(idx.trim()))); + } + return result; + } + } + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/FieldFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/FieldFilter.java new file mode 100644 index 00000000..79a9579c --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/FieldFilter.java @@ -0,0 +1,30 @@ +package com.jayway.jsonpath.reader.filter; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 10:17 PM + */ +public class FieldFilter extends Filter { + + public FieldFilter(String condition) { + super(condition); + } + + public Object filter(Object obj) { + if(isList(obj)){ + List result = new LinkedList(); + for (Object item : toList(obj)) { + result.add(filter(item)); + } + return result; + } else { + return getMapValue(obj, condition); + } + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/Filter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/Filter.java new file mode 100644 index 00000000..8d9b53bc --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/Filter.java @@ -0,0 +1,77 @@ +package com.jayway.jsonpath.reader.filter; + +import java.util.List; +import java.util.Map; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 10:14 PM + */ +public abstract class Filter { + + protected final String condition; + + public Filter(String condition) { + this.condition = condition; + } + + /** + * checks if object is instanceof java.util.List or java.util.Map + * + * @param obj object to check + * @return true if List or Map + */ + protected boolean isContainer(Object obj) { + return (isList(obj) || isMap(obj)); + } + + /** + * checks if object is instanceof java.util.List + * + * @param obj object to check + * @return true if List + */ + protected boolean isList(Object obj) { + return (obj instanceof List); + } + + protected List toList(Object list) { + return (List) list; + } + + protected Map toMap(Object map) { + return (Map) map; + } + + protected Object getMapValue(Object obj, String key) { + Map map = toMap(obj); + return map.get(key); + } + + /** + * checks if object is instanceof java.util.Map + * + * @param obj object to check + * @return true if Map + */ + protected boolean isMap(Object obj) { + return (obj instanceof Map); + } + + protected String trim(String str, int front, int end) { + String res = str; + + if (front > 0) { + res = str.substring(front); + } + if (end > 0) { + res = res.substring(0, res.length() - end); + } + return res; + } + + public abstract Object filter(Object obj); + +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/FilterFactory.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/FilterFactory.java new file mode 100644 index 00000000..e6041ee8 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/FilterFactory.java @@ -0,0 +1,52 @@ +package com.jayway.jsonpath.reader.filter; + +import com.jayway.jsonpath.filter.ListEvalFilter; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 10:13 PM + */ +public class FilterFactory { + + public static Filter createFilter(String pathFragment) { + + if ("$".equals(pathFragment) || "*".equals(pathFragment) || "[*]".equals(pathFragment)) { + + return new PassThrewFilter(pathFragment); + + } else if (pathFragment.contains("..")) { + + return new ScanFilter(pathFragment); + + } else if (!pathFragment.contains("[")) { + + return new FieldFilter(pathFragment); + + } else if (pathFragment.contains("[")) { + + if (pathFragment.startsWith("[?")) { + if(!pathFragment.contains("=")){ + //[?(@.isbn)] + return new HasFieldFilter(pathFragment); + } else { + //[?(@.name='foo')] + return new ArrayEvalFilter(pathFragment); + } + } else { + //[0] + //[0,1, ...] + //[-1:] + //[:1] + return new ArrayIndexFilter(pathFragment); + } + } + + throw new UnsupportedOperationException(".."); + + } + + + +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/HasFieldFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/HasFieldFilter.java new file mode 100644 index 00000000..1025692e --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/HasFieldFilter.java @@ -0,0 +1,38 @@ +package com.jayway.jsonpath.reader.filter; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/5/11 + * Time: 12:17 AM + */ +public class HasFieldFilter extends Filter { + + public HasFieldFilter(String condition) { + super(condition); + } + + @Override + public Object filter(Object obj) { + + //[?(@.isbn)] + List src = toList(obj); + List result = new LinkedList(); + + String trimmedCondition = trim(condition, 5, 2); + + for (Object item : src) { + if(isMap(item)){ + Map map = toMap(item); + if(map.containsKey(trimmedCondition)){ + result.add(map); + } + } + } + return result; + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/PassThrewFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/PassThrewFilter.java new file mode 100644 index 00000000..3d7b84e4 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/PassThrewFilter.java @@ -0,0 +1,18 @@ +package com.jayway.jsonpath.reader.filter; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 10:15 PM + */ +public class PassThrewFilter extends Filter { + + public PassThrewFilter(String condition) { + super(condition); + } + + public Object filter(Object obj) { + return obj; + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ScanFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ScanFilter.java new file mode 100644 index 00000000..637973b3 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/ScanFilter.java @@ -0,0 +1,48 @@ +package com.jayway.jsonpath.reader.filter; + +import com.jayway.jsonpath.JsonUtil; + +import java.util.LinkedList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/7/11 + * Time: 12:31 PM + */ +public class ScanFilter extends Filter { + + public ScanFilter(String condition) { + super(condition); + } + + @Override + public Object filter(Object obj) { + List result = new LinkedList(); + scan(obj, result); + + return result; + } + + + private void scan(Object container, List result) { + + if (isMap(container)) { + result.add(container); + + for (Object value : toMap(container).values()) { + if (isContainer(value)) { + scan(value, result); + } + } + } else if (isList(container)) { + + for (Object value : JsonUtil.toList(container)) { + if (isContainer(value)) { + scan(value, result); + } + } + } + } +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/PathTokenizerTest.java b/json-path/src/test/java/com/jayway/jsonpath/PathTokenizerTest.java new file mode 100644 index 00000000..5a2483f2 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/PathTokenizerTest.java @@ -0,0 +1,167 @@ +package com.jayway.jsonpath; + +import com.jayway.jsonpath.reader.PathToken; +import com.jayway.jsonpath.reader.PathTokenizer; +import org.junit.Test; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static org.hamcrest.Matchers.hasItems; +import static org.junit.Assert.assertThat; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/4/11 + * Time: 10:44 PM + */ +public class PathTokenizerTest { + + public final static String DOCUMENT = + "{ \"store\": {\n" + + " \"book\": [ \n" + + " { \"category\": \"reference\",\n" + + " \"author\": \"Nigel Rees\",\n" + + " \"title\": \"Sayings of the Century\",\n" + + " \"price\": 8.95\n" + + " },\n" + + " { \"category\": \"fiction\",\n" + + " \"author\": \"Evelyn Waugh\",\n" + + " \"title\": \"Sword of Honour\",\n" + + " \"price\": 12.99\n" + + " },\n" + + " { \"category\": \"fiction\",\n" + + " \"author\": \"Herman Melville\",\n" + + " \"title\": \"Moby Dick\",\n" + + " \"isbn\": \"0-553-21311-3\",\n" + + " \"price\": 8.99\n" + + " },\n" + + " { \"category\": \"fiction\",\n" + + " \"author\": \"J. R. R. Tolkien\",\n" + + " \"title\": \"The Lord of the Rings\",\n" + + " \"custom\": \"onely this\",\n" + + " \"isbn\": \"0-395-19395-8\",\n" + + " \"price\": 22.99\n" + + " }\n" + + " ],\n" + + " \"bicycle\": {\n" + + " \"color\": \"red\",\n" + + " \"price\": 19.95,\n" + + " \"foo:bar\": \"fooBar\",\n" + + " \"dot.notation\": \"new\"\n" + + " }\n" + + " }\n" + + "}"; + + @Test + public void path_tokens_can_be_read() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.bicycle.color")) { + result = pathToken.filter(result); + } + + assertEquals("red", result); + } + + @Test + public void read_an_array_without_filters() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book")) { + result = pathToken.filter(result); + } + + assertEquals(4, toList(result).size()); + } + + @Test + public void read_a_literal_property_from_object_in_array() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[*].title")) { + result = pathToken.filter(result); + } + + assertEquals(4, toList(result).size()); + } + + @Test + public void read_a_literal_property_from_position_in_array() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[0].title")) { + result = pathToken.filter(result); + } + + assertEquals("Sayings of the Century", result); + } + + @Test + public void read_a_literal_property_from_two_positions_in_array() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[0, 1].author")) { + result = pathToken.filter(result); + } + + assertThat(this.toList(result), hasItems("Nigel Rees", "Evelyn Waugh")); + } + + @Test + public void read_a_literal_property_from_head_in_array() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[:2].author")) { + result = pathToken.filter(result); + } + + assertThat(this.toList(result), hasItems("Nigel Rees", "Evelyn Waugh")); + } + + @Test + public void read_a_literal_property_from_tail_in_array() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[-1:].author")) { + result = pathToken.filter(result); + } + assertEquals("J. R. R. Tolkien", result); + } + + @Test + public void field_defined_in_array_object() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[?(@.custom)].author")) { + result = pathToken.filter(result); + } + assertThat(this.toList(result), hasItems("J. R. R. Tolkien")); + } + + @Test + public void property_value_in_array_object() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$.store.book[?(@.custom = 'onely this')].author")) { + result = pathToken.filter(result); + } + assertThat(this.toList(result), hasItems("J. R. R. Tolkien")); + } + + @Test + public void deep_scan() throws Exception { + Object result = JsonPath.parse(DOCUMENT); + + for (PathToken pathToken : new PathTokenizer("$..author")) { + result = pathToken.filter(result); + } + assertThat(this.toList(result), hasItems("Nigel Rees","Evelyn Waugh", "J. R. R. Tolkien")); + } + + private List toList(Object obj) { + return (List) obj; + } + +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/SplitPathFragmentsTest.java b/json-path/src/test/java/com/jayway/jsonpath/SplitPathFragmentsTest.java index e624580d..c4389456 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/SplitPathFragmentsTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/SplitPathFragmentsTest.java @@ -67,6 +67,8 @@ public class SplitPathFragmentsTest { assertPath("$.[ 'store' ]", hasItems("$", "store")); + assertPath("$.['store bore']", hasItems("$", "store bore")); + assertPath("$..book[ ?(@.price<10) ]", hasItems("$", "..", "book", "[?(@.price<10)]")); assertPath("$..book[?(@.price<10 )]", hasItems("$", "..", "book", "[?(@.price<10)]")); @@ -97,10 +99,13 @@ public class SplitPathFragmentsTest { private void assertPath(String path, Matcher> matcher) { System.out.println("PATH: " + path); + List fragments = PathUtil.splitPath(path); + for (String fragment : fragments) { System.out.println(fragment); } + assertThat(fragments, matcher); System.out.println("----------------------------------"); }