diff --git a/changelog.txt b/changelog.txt index e69de29b..f3d46a9f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -0,0 +1,8 @@ +------------------------------------------ +0.5.2 +------------------------------------------ + +- Fixed issue that path was never considered definite if containing a ':' +- Bracket notation is now first class citizen $.foo.bar == $.['foo'].['bar'] +- Added JsonAsserter.assertNotDefined(String path) to allow checks for negative existence of a path + diff --git a/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java b/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java index 0f0024ab..9cff5bec 100644 --- a/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java +++ b/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java @@ -16,7 +16,7 @@ public interface JsonAsserter { *

* * with(json).assertThat("items[0].name", equalTo("Bobby")) - * .assertThat("items[0].age" , equalTo(24L)) + * .assertThat("items[0].age" , equalTo(24L)) * * * @param path the json path specifying the value being compared @@ -37,6 +37,16 @@ public interface JsonAsserter { */ JsonAsserter assertEquals(String path, T expected); + /** + * Checks that a path is not defined within a document. If the document contains the + * given path, an AssertionError is thrown + * + * @param path the path to make sure not exists + * @return this + */ + JsonAsserter assertNotDefined(String path); + + /** * Asserts that object specified by path is null. If it is not, an AssertionError * is thrown with the given message. diff --git a/json-path-assert/src/main/java/com/jayway/jsonassert/impl/JsonAsserterImpl.java b/json-path-assert/src/main/java/com/jayway/jsonassert/impl/JsonAsserterImpl.java index aa5073c2..fb650aaf 100644 --- a/json-path-assert/src/main/java/com/jayway/jsonassert/impl/JsonAsserterImpl.java +++ b/json-path-assert/src/main/java/com/jayway/jsonassert/impl/JsonAsserterImpl.java @@ -4,13 +4,10 @@ package com.jayway.jsonassert.impl; import com.jayway.jsonassert.JsonAsserter; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathUtil; -import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; -import java.text.ParseException; -import java.util.List; - +import static java.lang.String.format; import static org.hamcrest.Matchers.*; /** @@ -21,7 +18,6 @@ import static org.hamcrest.Matchers.*; public class JsonAsserterImpl implements JsonAsserter { - private final Object jsonObject; @@ -35,20 +31,18 @@ public class JsonAsserterImpl implements JsonAsserter { } - /** * {@inheritDoc} */ @SuppressWarnings("unchecked") - public JsonAsserter assertThat(String path, Matcher matcher) { + public JsonAsserter assertThat(String path, Matcher matcher) { String reason = "When processing json path: " + path; - if(PathUtil.isPathDefinite(path)){ + if (PathUtil.isPathDefinite(path)) { MatcherAssert.assertThat(reason, JsonPath.readOne(jsonObject, path), matcher); - } - else { - MatcherAssert.assertThat(reason, (T) JsonPath.read(jsonObject, path), matcher); + } else { + MatcherAssert.assertThat(reason, (T) JsonPath.read(jsonObject, path), matcher); } return this; } @@ -60,6 +54,18 @@ public class JsonAsserterImpl implements JsonAsserter { return assertThat(path, equalTo(expected)); } + /** + * {@inheritDoc} + */ + public JsonAsserter assertNotDefined(String path) { + Object o = JsonPath.readOne(jsonObject, path); + + if(o != null){ + throw new AssertionError(format("Document contains the path <%s> but was expected not to.", path)); + } + return this; + } + /** * {@inheritDoc} */ diff --git a/json-path-assert/src/test/java/com/jayway/jsonassert/JsonAssertTest.java b/json-path-assert/src/test/java/com/jayway/jsonassert/JsonAssertTest.java index 31833e7f..513e5385 100644 --- a/json-path-assert/src/test/java/com/jayway/jsonassert/JsonAssertTest.java +++ b/json-path-assert/src/test/java/com/jayway/jsonassert/JsonAssertTest.java @@ -3,6 +3,8 @@ package com.jayway.jsonassert; import org.hamcrest.Matchers; import org.junit.Test; +import java.io.InputStream; + import static com.jayway.jsonassert.JsonAssert.*; import static org.hamcrest.Matchers.*; @@ -41,11 +43,40 @@ public class JsonAssertTest { " ],\n" + " \"bicycle\": {\n" + " \"color\": \"red\",\n" + - " \"price\": 19.95\n" + + " \"price\": 19.95\n," + + " \"nullValue\": null\n" + " }\n" + " }\n" + "}"; + + @Test + public void links_document() throws Exception { + + with(getResourceAsStream("links.json")).assertEquals("count", 2L) + .assertThat("links.gc:this.href", endsWith("?pageNumber=1&pageSize=2")) + .assertNotDefined("links.gc:prev") + .assertNotDefined("links.gc:next") + .assertThat("rows", collectionWithSize(equalTo(2))); + + } + + + @Test + public void a_document_can_be_expected_not_to_contain_a_path() throws Exception { + with(JSON).assertNotDefined("$.store.bicycle.cool"); + } + + @Test + public void a_value_can_asserted_to_be_null() throws Exception { + with(JSON).assertNull("$.store.bicycle.nullValue"); + } + + @Test + public void ends_with_evalueates() throws Exception { + with(JSON).assertThat("$.store.book[0].category", endsWith("nce")); + } + @Test public void a_path_can_be_asserted_with_matcher() throws Exception { @@ -78,6 +109,15 @@ public class JsonAssertTest { .assertThat("$.store.book[0]", mapContainingKey(equalTo("category"))) .and() .assertThat("$.store.book[0]", mapContainingValue(equalTo("reference"))); + + with(JSON).assertThat("$.['store'].['book'][0]", hasEntry("category", "reference")) + .assertThat("$.['store'].['book'][0]", hasEntry("title", "Sayings of the Century")) + .and() + .assertThat("$..['book'][0]", hasItems(hasEntry("category", "reference"))) + .and() + .assertThat("$.['store'].['book'][0]", mapContainingKey(equalTo("category"))) + .and() + .assertThat("$.['store'].['book'][0]", mapContainingValue(equalTo("reference"))); } @Test @@ -102,4 +142,9 @@ public class JsonAssertTest { with(JSON).assertThat("$.store.book[*].fooBar", emptyCollection()); } + + private InputStream getResourceAsStream(String resourceName) { + return getClass().getClassLoader().getResourceAsStream(resourceName); + } + } diff --git a/json-path-assert/src/test/resources/links.json b/json-path-assert/src/test/resources/links.json new file mode 100644 index 00000000..a3ecfac9 --- /dev/null +++ b/json-path-assert/src/test/resources/links.json @@ -0,0 +1,49 @@ +{ + "links": { + "gc:this": { + "rel": "gc:this", + "href": "/rest/account/market/incentives?pageNumber=1&pageSize=2" + } + }, + "count": 2, + "pageNumber": 1, + "pageSize": 2, + "rows": [{ + "id": "cd646745-5834-4244-9126-fa631c32693c", + "price": 10, + "title": "A TITLE", + "description": "description", + "smallIcon": "small.ico", + "categories": ["cd646745-5834-4244-9126-fa631c32693c"], + "brand": { + "id": "8d0280ae-8ea3-4ae1-b46c-f0fc0b293066", + "name": "brand.name", + "picture": "brand.picture" + }, + "links": { + "gc:market:incentive": { + "rel": "gc:market:incentive", + "href": "/rest/account/market/incentives/cd646745-5834-4244-9126-fa631c32693c" + } + } + }, + { + "id": "cd646745-5834-4244-9126-fa631c32693c2", + "price": 10, + "title": "B TITLE", + "description": "description", + "smallIcon": "small.ico", + "categories": ["cd646745-5834-4244-9126-fa631c32693c"], + "brand": { + "id": "8d0280ae-8ea3-4ae1-b46c-f0fc0b293066", + "name": "brand.name", + "picture": "brand.picture" + }, + "links": { + "gc:market:incentive": { + "rel": "gc:market:incentive", + "href": "/rest/account/market/incentives/cd646745-5834-4244-9126-fa631c32693c2" + } + } + }] +} 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 a43056ac..fb868c4b 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/PathUtil.java +++ b/json-path/src/main/java/com/jayway/jsonpath/PathUtil.java @@ -32,7 +32,8 @@ public class PathUtil { * @return true if path is definite (points to single item) */ public static boolean isPathDefinite(String jsonPath) { - return !jsonPath.replaceAll("\"[^\"\\\\\\n\r]*\"", "").matches(".*(\\.\\.|\\*|\\[[\\\\/]|\\?|,|:|>|\\(|<|=|\\+).*"); + //return !jsonPath.replaceAll("\"[^\"\\\\\\n\r]*\"", "").matches(".*(\\.\\.|\\*|\\[[\\\\/]|\\?|,|:|>|\\(|<|=|\\+).*"); + return !jsonPath.replaceAll("\"[^\"\\\\\\n\r]*\"", "").matches(".*(\\.\\.|\\*|\\[[\\\\/]|\\?|,|:\\s?\\]|\\[\\s?:|>|\\(|<|=|\\+).*"); } /** diff --git a/json-path/src/main/java/com/jayway/jsonpath/filter/JsonPathFilterFactory.java b/json-path/src/main/java/com/jayway/jsonpath/filter/JsonPathFilterFactory.java index 3ed84afe..97ffe254 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/filter/JsonPathFilterFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/filter/JsonPathFilterFactory.java @@ -10,8 +10,8 @@ import java.util.regex.Pattern; public class JsonPathFilterFactory { private final static Pattern ROOT_FILTER_PATTERN = Pattern.compile("\\$"); - private final static Pattern PROPERTY_FILTER_PATTERN = Pattern.compile("(\\w+)|\\['(\\w+)'\\]"); - //private final static Pattern PROPERTY_FILTER_PATTERN = Pattern.compile("\\w+"); + //private final static Pattern PROPERTY_FILTER_PATTERN = Pattern.compile("(\\w+)|\\['(\\w+)'\\]"); + private final static Pattern PROPERTY_FILTER_PATTERN = Pattern.compile("(.*)|\\['(.*?)'\\]"); private final static Pattern WILDCARD_PROPERTY_FILTER_PATTERN = Pattern.compile("\\*"); private final static Pattern LIST_FILTER_PATTERN = Pattern.compile("\\[.*?\\]"); private final static Pattern TRAVERSE_FILTER_PATTERN = Pattern.compile("\\.\\."); @@ -26,15 +26,15 @@ public class JsonPathFilterFactory { if(ROOT_FILTER_PATTERN.matcher(pathFragment).matches()){ return ROOT_FILTER; } - else if(PROPERTY_FILTER_PATTERN.matcher(pathFragment).matches() || WILDCARD_PROPERTY_FILTER_PATTERN.matcher(pathFragment).matches() ){ - return new PropertyFilter(pathFragment); - } else if(LIST_FILTER_PATTERN.matcher(pathFragment).matches()){ return new ListFilter(pathFragment); } else if(TRAVERSE_FILTER_PATTERN.matcher(pathFragment).matches()){ return TRAVERSE_FILTER; } + else if(PROPERTY_FILTER_PATTERN.matcher(pathFragment).matches() || WILDCARD_PROPERTY_FILTER_PATTERN.matcher(pathFragment).matches() ){ + return new PropertyFilter(pathFragment); + } return null; } 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 7c17be50..86be95a3 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java @@ -1,6 +1,5 @@ package com.jayway.jsonpath; -import org.apache.commons.lang.StringUtils; import org.junit.Test; import java.util.List; @@ -46,11 +45,19 @@ public class JsonPathTest { " ],\n" + " \"bicycle\": {\n" + " \"color\": \"red\",\n" + - " \"price\": 19.95\n" + + " \"price\": 19.95,\n" + + " \"foo:bar\": \"fooBar\"\n" + " }\n" + " }\n" + "}"; + @Test + public void read_path_with_colon() throws Exception { + + assertEquals(JsonPath.readOne(DOCUMENT, "$.store.bicycle.foo:bar"), "fooBar"); + assertEquals(JsonPath.readOne(DOCUMENT, "$.['store'].['bicycle'].['foo:bar']"), "fooBar"); + } + @Test public void read_document_from_root() throws Exception { @@ -165,11 +172,12 @@ public class JsonPathTest { assertTrue(res.isEmpty()); } - + /* @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 { diff --git a/json-path/src/test/java/com/jayway/jsonpath/PathUtilTest.java b/json-path/src/test/java/com/jayway/jsonpath/PathUtilTest.java index a609e8d4..0f183f50 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/PathUtilTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/PathUtilTest.java @@ -3,6 +3,7 @@ package com.jayway.jsonpath; import org.junit.Test; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * Created by IntelliJ IDEA. @@ -18,5 +19,12 @@ public class PathUtilTest { assertFalse(PathUtil.isPathDefinite("$..book[0]")); } + @Test + public void is_definite() throws Exception { + assertTrue(PathUtil.isPathDefinite("$.definite.this.is")); + assertTrue(PathUtil.isPathDefinite("$.definite:this.is")); + assertTrue(PathUtil.isPathDefinite("rows[0].id")); + } + } 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 e0270ba4..a5650bee 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/SplitPathFragmentsTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/SplitPathFragmentsTest.java @@ -60,6 +60,8 @@ public class SplitPathFragmentsTest { assertThat(PathUtil.splitPath("$.[0][1].author"), hasItems("$", "[0]", "[1]", "author")); assertThat(PathUtil.splitPath("$.[0].[1].author"), hasItems("$", "[0]", "[1]", "author")); + + assertThat(PathUtil.splitPath("$.foo:bar.author"), hasItems("$", "foo:bar", "author")); }