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 6e14afec..056b927f 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java +++ b/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java @@ -1,15 +1,16 @@ package com.jayway.jsonpath; -import com.jayway.jsonpath.filter.FilterOutput; -import com.jayway.jsonpath.filter.JsonPathFilterChain; -import net.minidev.json.JSONArray; -import net.minidev.json.JSONObject; +import com.jayway.jsonpath.reader.PathToken; +import com.jayway.jsonpath.reader.PathTokenizer; import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; import java.io.IOException; +import java.util.List; +import java.util.Map; import java.util.logging.Logger; +import java.util.regex.Pattern; /** * User: kalle stenflo @@ -84,16 +85,18 @@ public class JsonPath { private static JSONParser JSON_PARSER = new JSONParser(JsonPath.mode); - private JsonPathFilterChain filters; + private static Pattern DEFINITE_PATH_PATTERN = Pattern.compile(".*(\\.\\.|\\*|\\[[\\\\/]|\\?|,|:\\s?\\]|\\[\\s?:|>|\\(|<|=|\\+).*"); - public static void setMode(int mode){ - if(mode != JsonPath.mode){ + private PathTokenizer tokenizer; + + public static void setMode(int mode) { + if (mode != JsonPath.mode) { JsonPath.mode = mode; JSON_PARSER = new JSONParser(JsonPath.mode); } } - public static int getMode(){ + public static int getMode() { return mode; } @@ -111,24 +114,71 @@ public class JsonPath { throw new InvalidPathException("Invalid path"); } - this.filters = new JsonPathFilterChain(PathUtil.splitPath(jsonPath)); + this.tokenizer = new PathTokenizer(jsonPath); + //this.filters = new JsonPathFilterChain(PathUtil.splitPath(jsonPath)); + } + + public String getPath() { + return this.tokenizer.getPath(); } /** - * Applies this json path to the provided object + * Checks if a path points to a single item or if it potentially returns multiple items + *

+ * a path is considered not definite if it contains a scan fragment ".." + * or an array position fragment that is not based on a single index + *

+ *

+ * definite path examples are: + *

+ * $store.book + * $store.book[1].title + *

+ * not definite path examples are: + *

+ * $..book + * $.store.book[1,2] + * $.store.book[?(@.category = 'fiction')] * - * @param json a json Object + * @return true if path is definite (points to single item) + */ + public boolean isPathDefinite() { + //return !getPath().replaceAll("\"[^\"\\\\\\n\r]*\"", "").matches(".*(\\.\\.|\\*|\\[[\\\\/]|\\?|,|:\\s?\\]|\\[\\s?:|>|\\(|<|=|\\+).*"); + + String preparedPath = getPath().replaceAll("\"[^\"\\\\\\n\r]*\"", ""); + + return !DEFINITE_PATH_PATTERN.matcher(preparedPath).matches(); + + } + + /** + * Applies this container path to the provided object + * + * @param container a container Object * @param * @return list of objects matched by the given path */ - public T read(Object json) { - FilterOutput filterOutput = filters.filter(json); + public T read(Object container) { + + if(!(container instanceof Map) && !(container instanceof List) ){ + throw new IllegalArgumentException("Invalid container object"); + } + + Object result = container; + + for (PathToken pathToken : tokenizer) { + result = pathToken.filter(result); + } + return (T)result; + /* + FilterOutput filterOutput = filters.filter(container); if (filterOutput == null || filterOutput.getResult() == null) { return null; } return (T) filterOutput.getResult(); + */ } /** 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 index e3f97c07..f386f96c 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/reader/PathTokenizer.java +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/PathTokenizer.java @@ -14,6 +14,7 @@ import java.util.List; */ public class PathTokenizer implements Iterable { + private String path; private char[] pathChars; private int index = 0; private List pathTokens = new LinkedList(); @@ -23,13 +24,18 @@ public class PathTokenizer implements Iterable { if (!jsonPath.startsWith("$") && !jsonPath.startsWith("$[")) { jsonPath = "$." + jsonPath; } - pathChars = jsonPath.toCharArray(); + this.path = jsonPath; + this.pathChars = path.toCharArray(); for (String pathFragment : splitPath()) { pathTokens.add(new PathToken(pathFragment)); } } + public String getPath() { + return path; + } + public Iterator iterator() { return pathTokens.iterator(); } 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 index a6da0b48..781ad048 100644 --- 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 @@ -34,6 +34,8 @@ public class ArrayEvalFilter extends Filter { ConditionStatement conditionStatement = createConditionStatement(trimmedCondition); + + for (Object item : src) { if (isMatch(item, conditionStatement)) { result.add(item); @@ -83,6 +85,10 @@ public class ArrayEvalFilter extends Filter { this.field = field; this.operator = operator; this.expected = expected; + + if(this.expected.startsWith("'")){ + this.expected = trim(this.expected, 1, 1); + } } public String getField() { 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 index a51beec5..3d3bb00d 100644 --- 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 @@ -22,6 +22,13 @@ public class ArrayIndexFilter extends Filter { String trimmedCondition = trim(condition, 1, 1); + if(trimmedCondition.contains("@.length")){ + trimmedCondition = trim(trimmedCondition, 1, 1); + trimmedCondition = trimmedCondition.replace("@.length", ""); + trimmedCondition = trimmedCondition + ":"; + } + + if (trimmedCondition.startsWith(":")) { trimmedCondition = trim(trimmedCondition, 1, 0); int get = Integer.parseInt(trimmedCondition); @@ -34,6 +41,7 @@ public class ArrayIndexFilter extends Filter { trimmedCondition = trim(trimmedCondition, 1, 1); int get = Integer.parseInt(trimmedCondition); return src.get(src.size() - get); + } else { String[] indexArr = trimmedCondition.split(","); 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 index 79a9579c..de8f4d87 100644 --- 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 @@ -17,10 +17,20 @@ public class FieldFilter extends Filter { } public Object filter(Object obj) { - if(isList(obj)){ + if (isList(obj)) { List result = new LinkedList(); - for (Object item : toList(obj)) { - result.add(filter(item)); + for (Object current : toList(obj)) { + if (isMap(current)) { + Map map = toMap(current); + if (map.containsKey(condition)) { + Object o = map.get(condition); + if (isList(o)) { + result.addAll(toList(o)); + } else { + result.add(map.get(condition)); + } + } + } } return result; } else { 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 index e6041ee8..45f6ebd7 100644 --- 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 @@ -12,10 +12,14 @@ public class FilterFactory { public static Filter createFilter(String pathFragment) { - if ("$".equals(pathFragment) || "*".equals(pathFragment) || "[*]".equals(pathFragment)) { + if ("$".equals(pathFragment) || "[*]".equals(pathFragment)) { return new PassThrewFilter(pathFragment); + } else if ("*".equals(pathFragment)) { + + return new WildcardFilter(pathFragment); + } else if (pathFragment.contains("..")) { return new ScanFilter(pathFragment); @@ -27,7 +31,7 @@ public class FilterFactory { } else if (pathFragment.contains("[")) { if (pathFragment.startsWith("[?")) { - if(!pathFragment.contains("=")){ + if(!pathFragment.contains("=") && !pathFragment.contains("<") && !pathFragment.contains(">")){ //[?(@.isbn)] return new HasFieldFilter(pathFragment); } else { diff --git a/json-path/src/main/java/com/jayway/jsonpath/reader/filter/WildcardFilter.java b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/WildcardFilter.java new file mode 100644 index 00000000..447fee3d --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/reader/filter/WildcardFilter.java @@ -0,0 +1,38 @@ +package com.jayway.jsonpath.reader.filter; + +import com.jayway.jsonpath.JsonUtil; +import net.minidev.json.JSONArray; + +import java.util.LinkedList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 11/7/11 + * Time: 1:59 PM + */ +public class WildcardFilter extends Filter { + + public WildcardFilter(String condition) { + super(condition); + } + + @Override + public Object filter(Object obj) { + List result = new LinkedList(); + + if (isList(obj)) { + for (Object current : toList(obj)) { + for (Object value : toMap(current).values()) { + result.add(value); + } + } + } else { + for (Object value : toMap(obj).values()) { + result.add(value); + } + } + return result; + } +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java b/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java index ec060170..3fb92602 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java @@ -196,10 +196,20 @@ public class JsonPathTest { @Test public void all_books_cheaper_than_10() throws Exception { - assertThat(JsonPath.>read(DOCUMENT, "$.store.book[?(@.price < 10)].title"), hasItems("Sayings of the Century", "Moby Dick")); + assertThat(JsonPath.>read(DOCUMENT, "$..book[?(@.price<10)].title"), hasItems("Sayings of the Century", "Moby Dick")); } + @Test + public void all_books() throws Exception { + + //List books = JsonPath.>read(DOCUMENT, "$..book"); + Object books = JsonPath.>read(DOCUMENT, "$..book"); + + System.out.println("test"); + + } + @Test public void dot_in_predicate_works() throws Exception { @@ -217,7 +227,8 @@ public class JsonPathTest { @Test public void all_books_with_category_reference() throws Exception { - assertThat(JsonPath.>read(DOCUMENT, "$..book[?(@.category = 'reference')].title"), hasItems("Sayings of the Century")); + assertThat(JsonPath.>read(DOCUMENT, "$..book[?(@.category='reference')].title"), hasItems("Sayings of the Century")); + assertThat(JsonPath.>read(DOCUMENT, "$.store.book[?(@.category='reference')].title"), hasItems("Sayings of the Century")); } @@ -226,26 +237,12 @@ public class JsonPathTest { List all = JsonPath.read(DOCUMENT, "$..*"); } - @Test + @Test(expected = IndexOutOfBoundsException.class) public void access_index_out_of_bounds_does_not_throw_exception() throws Exception { Object res = JsonPath.read(DOCUMENT, "$.store.book[100].author"); - assertNull(res); - - res = JsonPath.read(DOCUMENT, "$.store.book[1, 200].author"); - - - assertThat((List) res, hasItems("Evelyn Waugh")); - //assertNull((); - } - - /* - @Test(expected = InvalidPathException.class) - public void invalid_space_path_throws_exception() throws Exception { - JsonPath.read(DOCUMENT, "space is not good"); } - */ @Test(expected = InvalidPathException.class) public void invalid_new_path_throws_exception() throws Exception {