From 50da66a744401b08865c153d350918b2e0ac298c Mon Sep 17 00:00:00 2001 From: kalle Date: Tue, 1 Feb 2011 15:35:54 +0100 Subject: [PATCH] started a simplistic JSONPath implementation --- json-assert/pom.xml | 4 + .../com/jayway/jsonassert/JSONAssert.java | 26 +- .../com/jayway/jsonassert/JSONAsserter.java | 17 +- .../{JSONReader.java => JsonPath.java} | 17 +- .../jsonassert/impl/JSONAsserterImpl.java | 23 +- .../com/jayway/jsonassert/impl/JSONPath.java | 40 --- .../jsonassert/impl/JSONPathFragment.java | 69 ----- .../jsonassert/impl/JSONReaderImpl.java | 113 ++++++- .../jayway/jsonassert/impl/JsonPathImpl.java | 185 ++++++++++++ .../java/com/jayway/jsonassert/impl/Path.java | 72 +++++ .../jayway/jsonassert/impl/PathFragment.java | 122 ++++++++ .../com/jayway/jsonassert/JSONAssertTest.java | 18 +- .../com/jayway/jsonassert/JSONReaderTest.java | 157 ++++++---- .../com/jayway/jsonassert/JsonPathTest.java | 278 ++++++++++++++++++ .../src/test/resources/json_array.json | 27 ++ pom.xml | 6 + 16 files changed, 947 insertions(+), 227 deletions(-) rename json-assert/src/main/java/com/jayway/jsonassert/{JSONReader.java => JsonPath.java} (73%) delete mode 100644 json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPath.java delete mode 100644 json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPathFragment.java create mode 100644 json-assert/src/main/java/com/jayway/jsonassert/impl/JsonPathImpl.java create mode 100644 json-assert/src/main/java/com/jayway/jsonassert/impl/Path.java create mode 100644 json-assert/src/main/java/com/jayway/jsonassert/impl/PathFragment.java create mode 100644 json-assert/src/test/java/com/jayway/jsonassert/JsonPathTest.java create mode 100644 json-assert/src/test/resources/json_array.json diff --git a/json-assert/pom.xml b/json-assert/pom.xml index 00791520..6e9a81d7 100644 --- a/json-assert/pom.xml +++ b/json-assert/pom.xml @@ -30,6 +30,10 @@ http://maven.apache.org + + commons-jxpath + commons-jxpath + com.googlecode.json-simple diff --git a/json-assert/src/main/java/com/jayway/jsonassert/JSONAssert.java b/json-assert/src/main/java/com/jayway/jsonassert/JSONAssert.java index e8bd8c48..2b2a124d 100644 --- a/json-assert/src/main/java/com/jayway/jsonassert/JSONAssert.java +++ b/json-assert/src/main/java/com/jayway/jsonassert/JSONAssert.java @@ -1,7 +1,7 @@ package com.jayway.jsonassert; -import com.jayway.jsonassert.impl.JSONAsserterImpl; -import com.jayway.jsonassert.impl.JSONReaderImpl; +import com.jayway.jsonassert.impl.JsonAsserterImpl; +import com.jayway.jsonassert.impl.JsonReaderImpl; import java.io.IOException; import java.io.Reader; @@ -12,7 +12,7 @@ import java.text.ParseException; * Date: 1/24/11 * Time: 9:31 PM */ -public class JSONAssert { +public class JsonAssert { /** * Creates a JSONReader @@ -21,8 +21,8 @@ public class JSONAssert { * @return a new reader * @throws ParseException */ - public static JSONReader parse(String jsonDoc) throws ParseException { - return JSONReaderImpl.parse(jsonDoc); + public static JsonPath parse(String jsonDoc) throws ParseException { + return JsonReaderImpl.parse(jsonDoc); } /** @@ -33,7 +33,7 @@ public class JSONAssert { * @throws ParseException document could not pe parsed * @throws IOException */ - public static JSONReader parse(Reader reader) throws ParseException, IOException { + public static JsonPath parse(Reader reader) throws ParseException, IOException { return parse(reader, false); } @@ -45,10 +45,10 @@ public class JSONAssert { * @throws ParseException document could not pe parsed * @throws IOException */ - public static JSONReader parse(Reader reader, boolean closeReader) throws ParseException, IOException { - JSONReader jsonReader = null; + public static JsonPath parse(Reader reader, boolean closeReader) throws ParseException, IOException { + JsonPath jsonReader = null; try { - jsonReader = JSONReaderImpl.parse(reader); + jsonReader = JsonReaderImpl.parse(reader); } finally { if(closeReader){ try { @@ -66,8 +66,8 @@ public class JSONAssert { * @return a JSON asserter initialized with the provided document * @throws ParseException when the given JSON could not be parsed */ - public static JSONAsserter with(String json) throws ParseException { - return new JSONAsserterImpl(JSONReaderImpl.parse(json)); + public static JsonAsserter with(String json) throws ParseException { + return new JsonAsserterImpl(JsonReaderImpl.parse(json)); } /** @@ -77,8 +77,8 @@ public class JSONAssert { * @return a JSON asserter initialized with the provided document * @throws ParseException when the given JSON could not be parsed */ - public static JSONAsserter with(Reader reader) throws ParseException, IOException { - return new JSONAsserterImpl(JSONReaderImpl.parse(reader)); + public static JsonAsserter with(Reader reader) throws ParseException, IOException { + return new JsonAsserterImpl(JsonReaderImpl.parse(reader)); } } diff --git a/json-assert/src/main/java/com/jayway/jsonassert/JSONAsserter.java b/json-assert/src/main/java/com/jayway/jsonassert/JSONAsserter.java index 18414d7a..27b28c3f 100644 --- a/json-assert/src/main/java/com/jayway/jsonassert/JSONAsserter.java +++ b/json-assert/src/main/java/com/jayway/jsonassert/JSONAsserter.java @@ -3,19 +3,18 @@ package com.jayway.jsonassert; import org.hamcrest.Matcher; /** - * Created by IntelliJ IDEA. * User: kallestenflo * Date: 1/24/11 * Time: 9:22 PM */ -public interface JSONAsserter { +public interface JsonAsserter { /** - * Gives access to the {@link JSONReader} used to base the assertions on + * Gives access to the {@link JsonPath} used to base the assertions on * * @return the underlying reader */ - JSONReader reader(); + JsonPath reader(); /** * Asserts that object specified by path satisfies the condition specified by matcher. @@ -32,7 +31,7 @@ public interface JSONAsserter { * @param the static type accepted by the matcher * @return this to allow fluent assertion chains */ - JSONAsserter assertThat(String path, Matcher matcher); + JsonAsserter assertThat(String path, Matcher matcher); /** * Asserts that object specified by path is equal to the expected value. @@ -43,7 +42,7 @@ public interface JSONAsserter { * @param the static type that should be returned by the path * @return this to allow fluent assertion chains */ - JSONAsserter assertEquals(String path, T expected); + JsonAsserter assertEquals(String path, T expected); /** * Asserts that object specified by path is null. If it is not, an AssertionError @@ -52,7 +51,7 @@ public interface JSONAsserter { * @param path the json path specifying the value that should be null * @return this to allow fluent assertion chains */ - JSONAsserter assertNull(String path); + JsonAsserter assertNull(String path); /** * Asserts that object specified by path is NOT null. If it is, an AssertionError @@ -61,7 +60,7 @@ public interface JSONAsserter { * @param path the json path specifying the value that should be NOT null * @return this to allow fluent assertion chains */ - JSONAsserter assertNotNull(String path); + JsonAsserter assertNotNull(String path); /** * Syntactic sugar to allow chaining assertions with a separating and() statement @@ -73,5 +72,5 @@ public interface JSONAsserter { * * @return this to allow fluent assertion chains */ - JSONAsserter and(); + JsonAsserter and(); } diff --git a/json-assert/src/main/java/com/jayway/jsonassert/JSONReader.java b/json-assert/src/main/java/com/jayway/jsonassert/JsonPath.java similarity index 73% rename from json-assert/src/main/java/com/jayway/jsonassert/JSONReader.java rename to json-assert/src/main/java/com/jayway/jsonassert/JsonPath.java index 41e14481..35e014bf 100644 --- a/json-assert/src/main/java/com/jayway/jsonassert/JSONReader.java +++ b/json-assert/src/main/java/com/jayway/jsonassert/JsonPath.java @@ -4,12 +4,19 @@ import java.util.List; import java.util.Map; /** - * Created by IntelliJ IDEA. * User: kallestenflo * Date: 1/24/11 * Time: 9:29 PM */ -public interface JSONReader { +public interface JsonPath { + + /** + * Get a new reader with the given path as root + * + * @param path to extract a reader for + * @return new reader + */ + JsonPath getReader(String path); /** * @param path @@ -25,9 +32,9 @@ public interface JSONReader { /** * @param path - * @return + * @returnÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ */ - Object get(String path); + T get(String path); /** * @param path @@ -64,5 +71,5 @@ public interface JSONReader { * @param path * @return */ - Map getMap(String path); + Map getMap(String path); } diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONAsserterImpl.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONAsserterImpl.java index 4b9d19a5..80c9a5c3 100644 --- a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONAsserterImpl.java +++ b/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONAsserterImpl.java @@ -1,8 +1,8 @@ package com.jayway.jsonassert.impl; -import com.jayway.jsonassert.JSONAsserter; -import com.jayway.jsonassert.JSONReader; +import com.jayway.jsonassert.JsonAsserter; +import com.jayway.jsonassert.JsonPath; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; @@ -13,9 +13,9 @@ import static org.hamcrest.Matchers.*; * Date: 1/21/11 * Time: 3:43 PM */ -public class JSONAsserterImpl implements JSONAsserter { +public class JsonAsserterImpl implements JsonAsserter { - private final JSONReader reader; + private final JsonPath reader; /** @@ -23,21 +23,22 @@ public class JSONAsserterImpl implements JSONAsserter { * * @param reader initialized with the JSON document to be asserted upon */ - public JSONAsserterImpl(JSONReader reader) { + public JsonAsserterImpl(JsonPath reader) { this.reader = reader; } /** * {@inheritDoc} */ - public JSONReader reader() { + public JsonPath reader() { return reader; } /** * {@inheritDoc} */ - public JSONAsserter assertThat(String path, Matcher matcher) { + @SuppressWarnings("unchecked") + public JsonAsserter assertThat(String path, Matcher matcher) { MatcherAssert.assertThat((T) reader.get(path), matcher); return this; } @@ -45,28 +46,28 @@ public class JSONAsserterImpl implements JSONAsserter { /** * {@inheritDoc} */ - public JSONAsserter assertEquals(String path, T expected) { + public JsonAsserter assertEquals(String path, T expected) { return assertThat(path, equalTo(expected)); } /** * {@inheritDoc} */ - public JSONAsserter assertNull(String path) { + public JsonAsserter assertNull(String path) { return assertThat(path, nullValue()); } /** * {@inheritDoc} */ - public JSONAsserter assertNotNull(String path) { + public JsonAsserter assertNotNull(String path) { return assertThat(path, notNullValue()); } /** * {@inheritDoc} */ - public JSONAsserter and() { + public JsonAsserter and() { return this; } diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPath.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPath.java deleted file mode 100644 index 07a8130d..00000000 --- a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPath.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.jayway.jsonassert.impl; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * User: kalle stenflo - * Date: 1/23/11 - * Time: 1:52 PM - */ -class JSONPath { - - private final Queue path; - - JSONPath(String path) { - this.path = compilePathFragments(path); - } - - boolean hasMoreFragments(){ - return !path.isEmpty(); - } - - JSONPathFragment nextFragment(){ - return path.poll(); - } - - private Queue compilePathFragments(String path) { - //TODO: this needs some attention but will work for now - Queue processed = new LinkedList(); - for (String fragment : path.split("[\\.|\\[]")) { - if (fragment.trim().length() > 0) { - - String compiledFragment = fragment.endsWith("]") ? "[" + fragment : fragment; - - processed.add(new JSONPathFragment(compiledFragment)); - } - } - return processed; - } -} diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPathFragment.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPathFragment.java deleted file mode 100644 index 0def3f30..00000000 --- a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONPathFragment.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.jayway.jsonassert.impl; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * User: kalle stenflo - * Date: 1/23/11 - * Time: 1:55 PM - */ -class JSONPathFragment { - - private static final Pattern ARRAY_POSITION_PATTER = Pattern.compile("\\[(\\d*)\\]"); - private static final Pattern GROOVY_POSITION_PATTER = Pattern.compile("get\\((\\d*)\\)"); - - private static final Pattern ARRAY_WILDCARD_PATTER = Pattern.compile("\\[\\*\\]"); - private static final Pattern GROOVY_WILDCARD_PATTER = Pattern.compile("get\\(\\*\\)"); - - private final String fragment; - - JSONPathFragment(String fragment) { - this.fragment = fragment; - } - - String value() { - return fragment; - } - - String appendToPath(String path){ - StringBuilder builder = new StringBuilder(path); - - if(ARRAY_POSITION_PATTER.matcher(fragment).matches()){ - builder.append("[").append(getArrayIndex()).append("]"); - } - else if(GROOVY_POSITION_PATTER.matcher(fragment).matches()){ - builder.append(path.isEmpty()?"":".").append("get(").append(getArrayIndex()).append(")"); - } - else if(ARRAY_WILDCARD_PATTER.matcher(fragment).matches()){ - builder.append("[*]"); - } - else if(GROOVY_WILDCARD_PATTER.matcher(fragment).matches()){ - builder.append(path.isEmpty()?"":".").append("get(*)"); - } - else { - builder.append(path.isEmpty()?"":".").append(fragment); - } - return builder.toString(); - } - - boolean isArrayIndex() { - return ARRAY_POSITION_PATTER.matcher(fragment).matches() || GROOVY_POSITION_PATTER.matcher(fragment).matches(); - } - - boolean isArrayWildcard() { - return ARRAY_WILDCARD_PATTER.matcher(fragment).matches() || GROOVY_WILDCARD_PATTER.matcher(fragment).matches(); - } - - int getArrayIndex() { - Matcher matcher = ARRAY_POSITION_PATTER.matcher(fragment); - if (matcher.matches()) { - return Integer.parseInt(matcher.group(1)); - } - matcher = GROOVY_POSITION_PATTER.matcher(fragment); - if (matcher.matches()) { - return Integer.parseInt(matcher.group(1)); - } - throw new IllegalArgumentException("not an array index fragment"); - } -} diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONReaderImpl.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONReaderImpl.java index 59dca68c..d61e51a7 100644 --- a/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONReaderImpl.java +++ b/json-assert/src/main/java/com/jayway/jsonassert/impl/JSONReaderImpl.java @@ -1,7 +1,7 @@ package com.jayway.jsonassert.impl; import com.jayway.jsonassert.InvalidPathException; -import com.jayway.jsonassert.JSONReader; +import com.jayway.jsonassert.JsonPath; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; @@ -20,7 +20,7 @@ import static org.apache.commons.lang.Validate.notNull; * Date: 1/20/11 * Time: 4:27 PM */ -public class JSONReaderImpl implements JSONReader { +public class JsonReaderImpl implements JsonPath { private static final JSONParser JSON_PARSER = new JSONParser(); @@ -29,9 +29,9 @@ public class JSONReaderImpl implements JSONReader { private String currentPath; - public static synchronized JSONReader parse(Reader reader) throws java.text.ParseException, IOException { + public static synchronized JsonPath parse(Reader reader) throws java.text.ParseException, IOException { try { - return new JSONReaderImpl(JSON_PARSER.parse(reader)); + return new JsonReaderImpl(JSON_PARSER.parse(reader)); } catch (IOException e) { throw e; } catch (ParseException e) { @@ -39,19 +39,32 @@ public class JSONReaderImpl implements JSONReader { } } - public static synchronized JSONReader parse(String jsonDoc) throws java.text.ParseException { + public static synchronized JsonPath parse(String jsonDoc) throws java.text.ParseException { try { - return new JSONReaderImpl(JSON_PARSER.parse(jsonDoc)); + return new JsonReaderImpl(JSON_PARSER.parse(jsonDoc)); } catch (ParseException e) { throw new java.text.ParseException(e.getMessage(), e.getPosition()); } } - private JSONReaderImpl(Object root) { + private JsonReaderImpl(Object root) { notNull(root, "root object can not be null"); this.root = root; } + /** + * {@inheritDoc} + */ + public JsonPath getReader(String path) { + + Object jsonObject = get(path); + + if (!isArray(jsonObject) && !isDocument(jsonObject)) { + throw new InvalidPathException("path points to a leaf that is not a JSON document or Array"); + } + + return new JsonReaderImpl(jsonObject); + } /** * {@inheritDoc} @@ -76,8 +89,9 @@ public class JSONReaderImpl implements JSONReader { /** * {@inheritDoc} */ - public Object get(String path) { - return getByPath(Object.class, path); + @SuppressWarnings("unchecked") + public T get(String path) { + return (T) getByPath(Object.class, path); } /** @@ -118,7 +132,7 @@ public class JSONReaderImpl implements JSONReader { /** * {@inheritDoc} */ - public Map getMap(String path) { + public Map getMap(String path) { return getByPath(Map.class, path); } @@ -129,7 +143,7 @@ public class JSONReaderImpl implements JSONReader { // //------------------------------------------------------------ - + /* private T getByPath(Class clazz, String stringPath) { currentPath = ""; Object current = this.root; @@ -137,20 +151,87 @@ public class JSONReaderImpl implements JSONReader { while (path.hasMoreFragments()) { - JSONPathFragment fragment = path.nextFragment(); + JSONPathFragment fragment = path.poll(); currentPath = fragment.appendToPath(currentPath); if (fragment.isArrayIndex()) { current = toArray(current).get(fragment.getArrayIndex()); } else if (fragment.isArrayWildcard()) { - current = getContainerValue(current, path.nextFragment()); + current = getContainerValue(current, path.poll()); } else { current = getContainerValue(current, fragment); } } return clazz.cast(current); } + */ + + ///* + private T getByPath(Class clazz, String stringPath) { + currentPath = ""; + Object current = this.root; + Path path = new Path(stringPath); + + while (path.hasMoreFragments()) { + + PathFragment fragment = path.poll(); + + currentPath = fragment.appendToPath(currentPath); + + if (fragment.isArrayIndex()) { + current = toArray(current).get(fragment.getArrayIndex()); + } else if (fragment.isArrayWildcard()) { + current = getContainerValue(current, path.poll()); + } else { + + System.out.println("FRAGMENT " + fragment.toString()); + + current = getContainerValue(current, fragment); + + if (isArray(current) && path.hasMoreFragments() && !path.peek().isArrayIndex() && !path.peek().isArrayWildcard()) { + + JSONArray array = new JSONArray(); + + for (Object o : toArray(current)) { + String newPath = path.toString(); + + System.out.println("NEW PATH " + newPath); + + if (isDocument(o) || isArray(o)) { + + JsonReaderImpl sub = new JsonReaderImpl(o); + + Object o1 = sub.get(newPath); + //if(o instanceof Collection){ + array.add(o1); + //} + //else { + // array.addAll(l) + //} + + + } else { + System.out.println("hhhhhhh"); + array.add(o); + } + } + current = array; + break; + } + } + } + return clazz.cast(current); + } + + //*/ + private boolean isArray(Object obj) { + return (obj instanceof JSONArray); + } + + private boolean isDocument(Object obj) { + return (obj instanceof JSONObject); + } private JSONArray toArray(Object array) { return (JSONArray) array; @@ -167,14 +248,14 @@ public class JSONReaderImpl implements JSONReader { * will be returned in a List * * @param container a json document or array - * @param fragment the field to extract from the document alt. the documents contained in the array + * @param fragment the field to extract from the document alt. the documents contained in the array * @return a single field value or a List of fields */ - private Object getContainerValue(Object container, JSONPathFragment fragment) { + private Object getContainerValue(Object container, PathFragment fragment) { Object result; if (container instanceof JSONArray) { - List list = new LinkedList(); + List list = new LinkedList(); for (Object doc : toArray(container)) { list.add(getContainerValue(doc, fragment)); } diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/JsonPathImpl.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/JsonPathImpl.java new file mode 100644 index 00000000..48afbf62 --- /dev/null +++ b/json-assert/src/main/java/com/jayway/jsonassert/impl/JsonPathImpl.java @@ -0,0 +1,185 @@ +package com.jayway.jsonassert.impl; + +import com.jayway.jsonassert.InvalidPathException; +import com.jayway.jsonassert.JsonPath; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.io.IOException; +import java.io.Reader; +import java.util.List; +import java.util.Map; + +/** + * User: kalle stenflo + * Date: 2/1/11 + * Time: 8:45 AM + * + * http://goessner.net/articles/JsonPath/ + */ +public class JsonPathImpl implements JsonPath { + + private static final JSONParser JSON_PARSER = new JSONParser(); + + private Object model; + + public static synchronized JsonPath parse(Reader reader) throws java.text.ParseException, IOException { + try { + return new JsonPathImpl(JSON_PARSER.parse(reader)); + } catch (IOException e) { + throw e; + } catch (ParseException e) { + throw new java.text.ParseException(e.getMessage(), e.getPosition()); + } + } + + public static synchronized JsonPath parse(String jsonDoc) throws java.text.ParseException { + try { + return new JsonPathImpl(JSON_PARSER.parse(jsonDoc)); + } catch (ParseException e) { + throw new java.text.ParseException(e.getMessage(), e.getPosition()); + } + } + + public JsonPathImpl(Object model) { + this.model = model; + } + + private void read(Object root, Path path, ReaderContext ctx, boolean strict) { + if (!path.hasMoreFragments()) { + ctx.addResult(root); + return; + } + if (isDocument(root)) { + PathFragment fragment = path.poll(); + + if(strict && !toDocument(root).containsKey(fragment.value())){ + throw new InvalidPathException(); + } + + Object extracted = toDocument(root).get(fragment.value()); + + if (fragment.isLeaf()) { + ctx.addResult(extracted); + } else { + read(extracted, path.clone(), ctx, strict); + } + } else { + PathFragment fragment = path.poll(); + + if (fragment.isArrayIndex()) { + Object extracted = toArray(root).get(fragment.getArrayIndex()); + + read(extracted, path.clone(), ctx, strict); + + } else if (fragment.isArrayWildcard()) { + ctx.split(); + + for (Object extracted : toArray(root)) { + read(extracted, path.clone(), ctx, strict); + } + } + } + } + + private static class ReaderContext { + private JSONArray result = new JSONArray(); + private boolean pathSplit = false; + + private void addResult(Object obj) { + result.add(obj); + } + + private void split() { + pathSplit = true; + } + + private boolean isPathSplit() { + return pathSplit; + } + } + + private T get(String jsonPath, boolean strict) { + Path path = new Path(jsonPath); + + ReaderContext ctx = new ReaderContext(); + + read(model, path.clone(), ctx, strict); + + if (ctx.isPathSplit()) { + return (T) ctx.result; + } else { + return (T) ctx.result.get(0); + } + } + + public JsonPath getReader(String path) { + + Object subModel = get(path); + + if (!isArray(subModel) && !isDocument(subModel)) { + throw new InvalidPathException(); + } + return new JsonPathImpl(subModel); + } + + public boolean hasJsonPath(String path) { + boolean hasPath = true; + try { + get(path, true); + } catch (Exception e) { + hasPath = false; + } + return hasPath; + } + + public boolean isNull(String path) { + return (get(path) == null); + } + + public T get(String jsonPath) { + return (T) get(jsonPath, false); + } + + public String getString(String path) { + return get(path); + } + + public Long getLong(String path) { + return get(path); + } + + public Double getDouble(String path) { + return get(path); + } + + public Boolean getBoolean(String path) { + return get(path); + } + + public List getList(String path) { + return get(path); + } + + public Map getMap(String path) { + return get(path); + } + + private boolean isArray(Object obj) { + return (obj instanceof JSONArray); + } + + private boolean isDocument(Object obj) { + return (obj instanceof JSONObject); + } + + private JSONArray toArray(Object array) { + return (JSONArray) array; + } + + private JSONObject toDocument(Object document) { + return (JSONObject) document; + } +} diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/Path.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/Path.java new file mode 100644 index 00000000..6ff54221 --- /dev/null +++ b/json-assert/src/main/java/com/jayway/jsonassert/impl/Path.java @@ -0,0 +1,72 @@ +package com.jayway.jsonassert.impl; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * User: kalle stenflo + * Date: 1/23/11 + * Time: 1:52 PM + */ +class Path { + + private final Queue path; + + Path(String path) { + this.path = compilePathFragments(path); + } + + Path(Queue path) { + this.path = new LinkedList(); + this.path.addAll(path); + } + + int size() { + return path.size(); + } + + boolean hasMoreFragments() { + return !path.isEmpty(); + } + + PathFragment poll() { + return path.poll(); + } + + PathFragment peek() { + return path.peek(); + } + + @Override + protected Path clone() { + return new Path(path); + } + + @Override + public String toString() { + String result = ""; + for (PathFragment fragment : path) { + result = fragment.appendToPath(result); + } + return result; + } + + private Queue compilePathFragments(String path) { + //TODO: this needs some attention but will work for now + Queue processed = new LinkedList(); + + String[] split = path.split("[\\.|\\[]"); + + for (int i = 0; i < split.length; i++) { + if (split[i].trim().length() > 0) { + + String compiledFragment = split[i].endsWith("]") ? "[" + split[i] : split[i]; + + boolean isLeaf = (i == split.length - 1); + + processed.add(new PathFragment(compiledFragment, isLeaf)); + } + } + return processed; + } +} diff --git a/json-assert/src/main/java/com/jayway/jsonassert/impl/PathFragment.java b/json-assert/src/main/java/com/jayway/jsonassert/impl/PathFragment.java new file mode 100644 index 00000000..9c2f98bd --- /dev/null +++ b/json-assert/src/main/java/com/jayway/jsonassert/impl/PathFragment.java @@ -0,0 +1,122 @@ +package com.jayway.jsonassert.impl; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * User: kalle stenflo + * Date: 1/23/11 + * Time: 1:55 PM + */ +class PathFragment { + + //matches array index accessers like : [1], [23] + private static final Pattern ARRAY_POSITION_PATTER = Pattern.compile("\\[(\\d*)\\]"); + + //matches groovy style array index accessers like : get(1), get(23) + private static final Pattern GROOVY_POSITION_PATTER = Pattern.compile("get\\((\\d*)\\)"); + + //matches array wildcard : [*] + private static final Pattern ARRAY_WILDCARD_PATTER = Pattern.compile("\\[\\*\\]"); + + //matches groovy style array wildcard : [*] + private static final Pattern GROOVY_WILDCARD_PATTER = Pattern.compile("get\\(\\*\\)"); + + private final String value; + + private final boolean leaf; + + /** + * Creates a new path fragment + * + * @param value value of path fragment + * @param leaf + */ + PathFragment(String value, boolean leaf) { + this.value = value; + this.leaf = leaf; + } + + @Override + public String toString() { + return "JSONPathFragment{" + + "value='" + value + '\'' + + '}'; + } + + /** + * + * @return the value of this path fragment + */ + String value() { + return value; + } + + /** + * Utility method to rebuild a path from path fragments + * + * @param path the path to append this path fragment to + * + * @return the new extended path + */ + String appendToPath(String path){ + StringBuilder builder = new StringBuilder(path); + + if(ARRAY_POSITION_PATTER.matcher(value).matches()){ + builder.append("[").append(getArrayIndex()).append("]"); + } + else if(GROOVY_POSITION_PATTER.matcher(value).matches()){ + builder.append(path.isEmpty()?"":".").append("get(").append(getArrayIndex()).append(")"); + } + else if(ARRAY_WILDCARD_PATTER.matcher(value).matches()){ + builder.append("[*]"); + } + else if(GROOVY_WILDCARD_PATTER.matcher(value).matches()){ + builder.append(path.isEmpty()?"":".").append("get(*)"); + } + else { + builder.append(path.isEmpty()?"":".").append(value); + } + return builder.toString(); + } + + boolean isLeaf(){ + return leaf; + } + + /** + * Check if this path fragment is a array index + * + * @return true if this fragment is an array index + */ + boolean isArrayIndex() { + return ARRAY_POSITION_PATTER.matcher(value).matches() || GROOVY_POSITION_PATTER.matcher(value).matches(); + } + + /** + * Check if this path fragment is an array wildcard + * + * @return true if this fragment is an array wildcard + */ + boolean isArrayWildcard() { + return ARRAY_WILDCARD_PATTER.matcher(value).matches() || GROOVY_WILDCARD_PATTER.matcher(value).matches(); + } + + /** + * returns the int index of this path fragment. If this is not an array index fragment + * an UnsupportedOperationException is thrown + * + * @return the array index of this fragment + */ + int getArrayIndex() { + Matcher matcher = ARRAY_POSITION_PATTER.matcher(value); + if (matcher.matches()) { + return Integer.parseInt(matcher.group(1)); + } + matcher = GROOVY_POSITION_PATTER.matcher(value); + if (matcher.matches()) { + return Integer.parseInt(matcher.group(1)); + } + throw new UnsupportedOperationException("not an array index fragment"); + } +} diff --git a/json-assert/src/test/java/com/jayway/jsonassert/JSONAssertTest.java b/json-assert/src/test/java/com/jayway/jsonassert/JSONAssertTest.java index 6c01f0fe..55b08f93 100644 --- a/json-assert/src/test/java/com/jayway/jsonassert/JSONAssertTest.java +++ b/json-assert/src/test/java/com/jayway/jsonassert/JSONAssertTest.java @@ -9,7 +9,7 @@ import static org.hamcrest.Matchers.*; * Date: 1/21/11 * Time: 4:04 PM */ -public class JSONAssertTest { +public class JsonAssertTest { private static String TEST_DOCUMENT = "{ \"nullField\" : null \"stringField\" : \"string-field\" , \"numberField\" : 1234 , \"booleanField\" : true , \"subDocument\" : {\"subField\" : \"sub-field\"} , \"stringList\" : [\"ONE\", \"TWO\"], \"objectList\" : [{\"subField\" : \"sub-field-0\"}, {\"subField\" : \"sub-field-1\"}], \"listList\" : [[\"0.0\", \"0.1\"], [\"1.0\", \"1.1\"]], }"; @@ -17,48 +17,48 @@ public class JSONAssertTest { @Test public void a_path_can_be_asserted_with_matcher() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertThat("stringField", equalTo("string-field")); + JsonAssert.with(TEST_DOCUMENT).assertThat("stringField", equalTo("string-field")); } @Test public void list_content_can_be_asserted_with_matcher() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertThat("stringList", hasItems("ONE", "TWO")); + JsonAssert.with(TEST_DOCUMENT).assertThat("stringList", hasItems("ONE", "TWO")); } @Test public void map_content_can_be_asserted_with_matcher() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertThat("subDocument", hasEntry("subField", "sub-field")); + JsonAssert.with(TEST_DOCUMENT).assertThat("subDocument", hasEntry("subField", "sub-field")); } @Test public void a_sub_document_can_asserted_on__by_path() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertThat("subDocument.subField", is(equalTo("sub-field"))); + JsonAssert.with(TEST_DOCUMENT).assertThat("subDocument.subField", is(equalTo("sub-field"))); } @Test public void a_path_can_be_asserted_equal_to() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertEquals("stringField", "string-field"); + JsonAssert.with(TEST_DOCUMENT).assertEquals("stringField", "string-field"); } @Test public void a_path_can_be_asserted_is_null() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertNull("nullField"); + JsonAssert.with(TEST_DOCUMENT).assertNull("nullField"); } @Test(expected = AssertionError.class) public void failed_assert_throws() throws Exception { - JSONAssert.with(TEST_DOCUMENT).assertThat("stringField", equalTo("SOME CRAP")); + JsonAssert.with(TEST_DOCUMENT).assertThat("stringField", equalTo("SOME CRAP")); } @Test public void multiple_asserts_can_be_chained() throws Exception { - JSONAssert.with(TEST_DOCUMENT) + JsonAssert.with(TEST_DOCUMENT) .assertThat("stringField", equalTo("string-field")) .assertThat("numberField", is(notNullValue())) .and() diff --git a/json-assert/src/test/java/com/jayway/jsonassert/JSONReaderTest.java b/json-assert/src/test/java/com/jayway/jsonassert/JSONReaderTest.java index fa46084e..ba765d78 100644 --- a/json-assert/src/test/java/com/jayway/jsonassert/JSONReaderTest.java +++ b/json-assert/src/test/java/com/jayway/jsonassert/JSONReaderTest.java @@ -1,13 +1,13 @@ package com.jayway.jsonassert; -import com.jayway.jsonassert.impl.JSONReaderImpl; -import org.hamcrest.Matcher; +import com.jayway.jsonassert.impl.JsonReaderImpl; import org.hamcrest.Matchers; import org.junit.Test; import java.io.InputStreamReader; import java.text.ParseException; import java.util.List; +import java.util.Map; import static org.junit.Assert.*; @@ -16,48 +16,56 @@ import static org.junit.Assert.*; * Date: 1/20/11 * Time: 9:31 AM */ -public class JSONReaderTest { +public class JsonReaderTest { - private static String TEST_DOCUMENT = "{ \"nullField\" : null \"stringField\" : \"string-field\" , \"numberField\" : 1234 , \"doubleField\" : 12.34 , \"booleanField\" : true , \"subDocument\" : {\"subField\" : \"sub-field\"} , \"stringList\" : [\"ONE\", \"TWO\"], \"objectList\" : [{\"subField\" : \"sub-field-0\"}, {\"subField\" : \"sub-field-1\"}], \"listList\" : [[\"0.0\", \"0.1\"], [\"1.0\", \"1.1\"]], }"; + private static String TEST_DOCUMENT = "{ \"nullField\" : null, \"stringField\" : \"string-field\" , \"numberField\" : 1234 , \"doubleField\" : 12.34 , \"booleanField\" : true , \"subDocument\" : {\"subField\" : \"sub-field\"} , \"stringList\" : [\"ONE\", \"TWO\"], \"objectList\" : [{\"subField\" : \"sub-field-0\", \"subDoc\": {\"subField\": \"A\"}}, {\"subField\" : \"sub-field-1\", \"subDoc\": {\"subField\": \"B\"}}], \"listList\" : [[\"0.0\", \"0.1\"], [\"1.0\", \"1.1\"]], }"; private static String TEST_DOCUMENT_ARRAY = "{ \"listList\" : [[\"0.0\", \"0.1\"], [\"1.0\", \"1.1\"]], }"; private static String TEST_DEEP_PATH_DOCUMENT = "{ \"a\" : { \"b\" : { \"c\" : { \"say\" : \"hello\" } } }}"; private static String TEST_ARRAY = "[{\"name\" : \"name0\"}, {\"name\" : \"name1\"}]"; + private static String TEST_DEEP_PATH_DOCUMENT_ARRAY = "{ \"arr0\" : [{ \"arr0_0\" : [{ \"arr0_0_0\" : [{\"val\": \"0A\"}, {\"val\": \"1A\"}] }, { \"arr0_0\" : [{ \"arr0_0_0\" : [{\"val\": \"1A\"}, {\"val\": \"1B\"}] }] } ] }"; + + + + @Test(expected = ParseException.class) public void invalid_json_not_accepted() throws Exception { - JSONReaderImpl.parse("not json"); + JsonReaderImpl.parse("not json"); } @Test public void reader_can_be_created_with_input_stream() throws Exception { - JSONReader reader = JSONAssert.parse(getInputStreamReader("json-test-doc.json"), true); + JsonPath reader = JsonAssert.parse(getInputStreamReader("json-test-doc.json"), true); assertEquals("donut", reader.getString("type")); + assertEquals("donut", reader.get("type")); assertThat(reader.getList("toppings"), Matchers.hasItems("Glazed", "Sugar")); + assertThat(reader.>get("toppings"), Matchers.hasItems("Glazed", "Sugar")); } @Test public void a_string_field_can_be_accessed() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("string-field", reader.getString("stringField")); + assertEquals("string-field", reader.get("stringField")); } @Test public void is_null_returns_true_for_null_fields() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertTrue(reader.isNull("nullField")); } @Test public void is_null_returns_false_for_not_null_fields() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertFalse(reader.isNull("stringField")); } @@ -65,183 +73,222 @@ public class JSONReaderTest { @Test public void a_long_field_can_be_accessed() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertTrue(1234L == reader.getLong("numberField")); + assertEquals(1234L, reader.get("numberField")); } @Test public void a_double_field_can_be_accessed() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals(12.34D, reader.getDouble("doubleField"), 0.001); + assertEquals(12.34D, reader.get("doubleField"), 0.001); + } @Test public void a_boolean_field_can_be_accessed() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals(true, reader.getBoolean("booleanField")); + assertEquals(true, reader.get("booleanField")); } @Test public void a_path_can_be_checked_for_existence() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DEEP_PATH_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DEEP_PATH_DOCUMENT); assertTrue(reader.hasJsonPath("a.b.c.say")); } @Test public void a_path_can_be_checked_for_non_existence() throws Exception { - - - - JSONReader reader = JSONAssert.parse(TEST_DEEP_PATH_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DEEP_PATH_DOCUMENT); assertFalse(reader.hasJsonPath("a.b.c.FOO")); } @Test public void a_string_field_can_be_accessed_in_a_nested_document() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("sub-field", reader.getString("subDocument.subField")); + assertEquals("sub-field", reader.get("subDocument.subField")); } @Test public void a_list_can_be_accessed_in_a_document() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); - - List l = reader.getList("stringList"); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); - assertEquals(2, l.size()); + assertEquals(2, reader.getList("stringList").size()); + assertEquals(2, reader.>get("stringList").size()); } @Test public void a_list_can_be_accessed_by_array_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("ONE", reader.getString("stringList[0]")); - assertEquals("TWO", reader.getString("stringList[1]")); + + assertEquals("ONE", reader.get("stringList[0]")); + assertEquals("TWO", reader.get("stringList[1]")); } @Test public void a_list_can_be_accessed_by_groovy_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("ONE", reader.getString("stringList.get(0)")); - assertEquals("TWO", reader.getString("stringList.get(1)")); + + assertEquals("ONE", reader.get("stringList.get(0)")); + assertEquals("TWO", reader.get("stringList.get(1)")); } @Test public void a_document_contained_in_a_list_can_be_accessed_by_array_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("sub-field-0", reader.getString("objectList[0].subField")); + assertEquals("sub-field-0", reader.get("objectList[0].subField")); } + @Test public void a_document_contained_in_a_list_can_be_accessed_by_groovy_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("sub-field-0", reader.getString("objectList.get(0).subField")); + assertEquals("sub-field-0", reader.get("objectList.get(0).subField")); } @Test public void an_array_in_an_array_can_be_accessed_by_array_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT_ARRAY); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT_ARRAY); assertEquals("0.0", reader.getString("listList[0][0]")); assertEquals("0.1", reader.getString("listList[0][1]")); assertEquals("1.0", reader.getString("listList[1][0]")); assertEquals("1.1", reader.getString("listList[1][1]")); + + assertEquals("0.0", reader.get("listList[0][0]")); + assertEquals("0.1", reader.get("listList[0][1]")); + assertEquals("1.0", reader.get("listList[1][0]")); + assertEquals("1.1", reader.get("listList[1][1]")); } @Test public void an_array_in_an_array_can_be_accessed_by_groovy_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT_ARRAY); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT_ARRAY); assertEquals("0.0", reader.getString("listList.get(0).get(0)")); assertEquals("0.1", reader.getString("listList.get(0).get(1)")); assertEquals("1.0", reader.getString("listList.get(1).get(0)")); assertEquals("1.1", reader.getString("listList.get(1).get(1)")); + + assertEquals("0.0", reader.get("listList.get(0).get(0)")); + assertEquals("0.1", reader.get("listList.get(0).get(1)")); + assertEquals("1.0", reader.get("listList.get(1).get(0)")); + assertEquals("1.1", reader.get("listList.get(1).get(1)")); } @Test public void an_array_with_documents_can_be_accessed_by_index() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_ARRAY); + JsonPath reader = JsonAssert.parse(TEST_ARRAY); assertEquals("name0", reader.getString("[0].name")); assertEquals("name1", reader.getString("[1].name")); + + assertEquals("name0", reader.get("[0].name")); + assertEquals("name1", reader.get("[1].name")); } + @Test public void a_nested_document_can_be_accessed_as_a_map() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("sub-field", reader.getMap("subDocument").get("subField")); - } - - @Test - public void every_thing_can_be_fetched_as_object() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); - - assertEquals(true, reader.get("booleanField")); - assertEquals(1234L, reader.get("numberField")); - assertEquals("string-field", reader.get("stringField")); + assertEquals("sub-field", reader.get("subDocument").get("subField")); } + @Test public void a_deep_document_path_can_be_read() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DEEP_PATH_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DEEP_PATH_DOCUMENT); assertEquals("hello", reader.getString("a.b.c.say")); + + assertEquals("hello", reader.get("a.b.c.say")); } @Test(expected = InvalidPathException.class) public void exception_is_thrown_when_field_is_not_found() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); - String s = reader.getString("invalidProperty"); + reader.getString("invalidProperty"); + } - System.out.println("S= " + s); + @Test(expected = InvalidPathException.class) + public void exception_is_thrown_when_field_is_not_found_with_get() throws Exception { + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); + reader.get("invalidProperty"); } @Test public void array_wildcard_property_extract() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); - List l = reader.getList("objectList[*].subField"); + assertEquals(2, reader.getList("objectList[*].subField").size()); - assertEquals(2, l.size()); + assertEquals(2, reader.>get("objectList[*].subField").size()); - /* - assertEquals(2, reader.getList("objectList[*.subField]").size()); + } + + @Test + public void array_wildcard_property_extract_new() throws Exception { + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); - assertEquals(2, reader.getList("objectList[subField='foo']").size()); + assertEquals("A", reader.getList("objectList.subDoc.subField").get(0)); + assertEquals("B", reader.getList("objectList.subDoc.subField").get(1)); - assertEquals(2, reader.getList("objectList[].subField#foo").size()); + assertEquals("A", reader.>get("objectList.subDoc.subField").get(0)); + assertEquals("B", reader.>get("objectList.subDoc.subField").get(1)); - */ } @Test public void list_to_string_returns_json() throws Exception { - JSONReader reader = JSONAssert.parse(TEST_DOCUMENT); - + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); assertEquals("[\"ONE\",\"TWO\"]", reader.getList("stringList").toString()); } + @Test + public void get_sub_reader_for_document() throws Exception { + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); + + assertEquals("sub-field", reader.getReader("subDocument").getString("subField")); + } + + @Test + public void get_sub_reader_for_list() throws Exception { + JsonPath reader = JsonAssert.parse(TEST_DOCUMENT); + + assertEquals("ONE", reader.getReader("stringList").get("[0]")); + } + + //---------------------------------------------------------- // // helpers diff --git a/json-assert/src/test/java/com/jayway/jsonassert/JsonPathTest.java b/json-assert/src/test/java/com/jayway/jsonassert/JsonPathTest.java new file mode 100644 index 00000000..5d6fd6be --- /dev/null +++ b/json-assert/src/test/java/com/jayway/jsonassert/JsonPathTest.java @@ -0,0 +1,278 @@ +package com.jayway.jsonassert; + +import com.jayway.jsonassert.impl.JsonPathImpl; +import org.junit.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Created by IntelliJ IDEA. + * User: kallestenflo + * Date: 2/1/11 + * Time: 10:08 AM + */ +public class JsonPathTest { + + private static String TEST_DOCUMENT = "{ \"nullField\" : null, \"stringField\" : \"string-field\" , \"numberField\" : 1234 , \"doubleField\" : 12.34 , \"booleanField\" : true , \"subDocument\" : {\"subField\" : \"sub-field\"} , \"stringList\" : [\"ONE\", \"TWO\"], \"objectList\" : [{\"subField\" : \"sub-field-0\", \"subDoc\": {\"subField\": \"A\"}}, {\"subField\" : \"sub-field-1\", \"subDoc\": {\"subField\": \"B\"}}], \"listList\" : [[\"0.0\", \"0.1\"], [\"1.0\", \"1.1\"]], }"; + private static String TEST_DOCUMENT_ARRAY = "{ \"listList\" : [[\"0.0\", \"0.1\"], [\"1.0\", \"1.1\"]], }"; + private static String TEST_DEEP_PATH_DOCUMENT = "{ \"a\" : { \"b\" : { \"c\" : { \"say\" : \"hello\" } } }}"; + private static String TEST_ARRAY = "[{\"name\" : \"name0\"}, {\"name\" : \"name1\"}]"; + private static String TEST_DEEP_PATH_DOCUMENT_ARRAY = "{ \"arr0\" : [{ \"arr0_0\" : [{ \"arr0_0_0\" : [{\"val\": \"0A\"}, {\"val\": \"1A\"}] }, { \"arr0_0\" : [{ \"arr0_0_0\" : [{\"val\": \"1A\"}, {\"val\": \"1B\"}] }] } ] }"; + + + @Test + public void a_string_field_can_be_accessed() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("string-field", reader.getString("stringField")); + assertEquals("string-field", reader.get("stringField")); + } + + @Test + public void is_null_returns_true_for_null_fields() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertTrue(reader.isNull("nullField")); + } + + @Test + public void is_null_returns_false_for_not_null_fields() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertFalse(reader.isNull("stringField")); + } + + + @Test + public void a_long_field_can_be_accessed() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertTrue(1234L == reader.getLong("numberField")); + assertEquals(1234L, reader.get("numberField")); + } + + @Test + public void a_double_field_can_be_accessed() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals(12.34D, reader.getDouble("doubleField"), 0.001); + assertEquals(12.34D, reader.get("doubleField"), 0.001); + + } + + @Test + public void a_boolean_field_can_be_accessed() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals(true, reader.getBoolean("booleanField")); + assertEquals(true, reader.get("booleanField")); + } + + @Test + public void a_path_can_be_checked_for_existence() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DEEP_PATH_DOCUMENT); + + assertTrue(reader.hasJsonPath("a.b.c.say")); + } + + @Test + public void a_path_can_be_checked_for_non_existence() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DEEP_PATH_DOCUMENT); + + assertFalse(reader.hasJsonPath("a.b.c.FOO")); + } + + @Test + public void a_string_field_can_be_accessed_in_a_nested_document() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("sub-field", reader.getString("subDocument.subField")); + assertEquals("sub-field", reader.get("subDocument.subField")); + } + + @Test + public void a_list_can_be_accessed_in_a_document() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals(2, reader.getList("stringList").size()); + assertEquals(2, reader.>get("stringList").size()); + } + + @Test + public void a_list_can_be_accessed_by_array_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("ONE", reader.getString("stringList[0]")); + assertEquals("TWO", reader.getString("stringList[1]")); + + assertEquals("ONE", reader.get("stringList[0]")); + assertEquals("TWO", reader.get("stringList[1]")); + } + + @Test + public void a_list_can_be_accessed_by_groovy_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + + assertEquals("ONE", reader.getString("stringList.get(0)")); + assertEquals("TWO", reader.getString("stringList.get(1)")); + + assertEquals("ONE", reader.get("stringList.get(0)")); + assertEquals("TWO", reader.get("stringList.get(1)")); + } + + @Test + public void a_document_contained_in_a_list_can_be_accessed_by_array_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("sub-field-0", reader.getString("objectList[0].subField")); + assertEquals("sub-field-0", reader.get("objectList[0].subField")); + } + + + @Test + public void a_document_contained_in_a_list_can_be_accessed_by_groovy_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("sub-field-0", reader.getString("objectList.get(0).subField")); + assertEquals("sub-field-0", reader.get("objectList.get(0).subField")); + } + + @Test + public void an_array_in_an_array_can_be_accessed_by_array_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT_ARRAY); + + assertEquals("0.0", reader.getString("listList[0][0]")); + + assertEquals("0.1", reader.getString("listList[0][1]")); + assertEquals("1.0", reader.getString("listList[1][0]")); + assertEquals("1.1", reader.getString("listList[1][1]")); + + assertEquals("0.0", reader.get("listList[0][0]")); + assertEquals("0.1", reader.get("listList[0][1]")); + assertEquals("1.0", reader.get("listList[1][0]")); + assertEquals("1.1", reader.get("listList[1][1]")); + + } + + @Test + public void an_array_in_an_array_can_be_accessed_by_groovy_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT_ARRAY); + + assertEquals("0.0", reader.getString("listList.get(0).get(0)")); + assertEquals("0.1", reader.getString("listList.get(0).get(1)")); + assertEquals("1.0", reader.getString("listList.get(1).get(0)")); + assertEquals("1.1", reader.getString("listList.get(1).get(1)")); + + assertEquals("0.0", reader.get("listList.get(0).get(0)")); + assertEquals("0.1", reader.get("listList.get(0).get(1)")); + assertEquals("1.0", reader.get("listList.get(1).get(0)")); + assertEquals("1.1", reader.get("listList.get(1).get(1)")); + } + + @Test + public void an_array_with_documents_can_be_accessed_by_index() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_ARRAY); + + assertEquals("name0", reader.getString("[0].name")); + assertEquals("name1", reader.getString("[1].name")); + + assertEquals("name0", reader.get("[0].name")); + assertEquals("name1", reader.get("[1].name")); + } + + + @Test + public void a_nested_document_can_be_accessed_as_a_map() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("sub-field", reader.getMap("subDocument").get("subField")); + + assertEquals("sub-field", reader.get("subDocument").get("subField")); + } + + + @Test + public void a_deep_document_path_can_be_read() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DEEP_PATH_DOCUMENT); + + assertEquals("hello", reader.getString("a.b.c.say")); + + assertEquals("hello", reader.get("a.b.c.say")); + } +/* + @Test(expected = InvalidPathException.class) + public void exception_is_thrown_when_field_is_not_found() throws Exception { + JSONReader reader = JsonQueryNew.parse(TEST_DOCUMENT); + + reader.getString("invalidProperty"); + } +*/ + + @Test + public void null_is_returned_when_field_is_not_found() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertNull(reader.getString("invalidProperty")); + } + + public void null_is_when_field_is_not_found_with_get() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertNull(reader.get("invalidProperty")); + } + + + @Test + public void array_wildcard_property_extract() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals(2, reader.getList("objectList[*].subField").size()); + + assertEquals(2, reader.>get("objectList[*].subField").size()); + + + } + + @Test + public void array_wildcard_property_extract_new() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("A", reader.getList("objectList[*].subDoc.subField").get(0)); + assertEquals("B", reader.getList("objectList[*].subDoc.subField").get(1)); + + assertEquals("A", reader.>get("objectList[*].subDoc.subField").get(0)); + assertEquals("B", reader.>get("objectList[*].subDoc.subField").get(1)); + + + } + + + @Test + public void list_to_string_returns_json() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("[\"ONE\",\"TWO\"]", reader.getList("stringList").toString()); + } + + + @Test + public void get_sub_reader_for_document() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("sub-field", reader.getReader("subDocument").getString("subField")); + } + + @Test + public void get_sub_reader_for_list() throws Exception { + JsonPath reader = JsonPathImpl.parse(TEST_DOCUMENT); + + assertEquals("ONE", reader.getReader("stringList").get("[0]")); + } + + +} + diff --git a/json-assert/src/test/resources/json_array.json b/json-assert/src/test/resources/json_array.json new file mode 100644 index 00000000..27cfd068 --- /dev/null +++ b/json-assert/src/test/resources/json_array.json @@ -0,0 +1,27 @@ + + + +{ + "arr": [ + { + "obj":{"innerArr": [ + { + "val": "bla_0" + }, + { + "val": null + } + ]} + }, + { + "obj":{"innerArr": [ + { + "val": "bla_4" + }, + { + "val": "bla_5" + } + ]} + } + ] +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a2bea242..615a3c07 100644 --- a/pom.xml +++ b/pom.xml @@ -130,6 +130,12 @@ + + commons-jxpath + commons-jxpath + 1.3 + + com.googlecode.json-simple json-simple