Browse Source

deep scan fixes and tests

pull/142/head
Alexey Makeyev 9 years ago
parent
commit
f03875784e
  1. 16
      json-path/src/main/java/com/jayway/jsonpath/internal/token/ArrayPathToken.java
  2. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PredicatePathToken.java
  3. 6
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PropertyPathToken.java
  4. 6
      json-path/src/main/java/com/jayway/jsonpath/internal/token/ScanPathToken.java
  5. 88
      json-path/src/test/java/com/jayway/jsonpath/DeepScanTest.java
  6. 32
      json-path/src/test/java/com/jayway/jsonpath/TestUtils.java

16
json-path/src/main/java/com/jayway/jsonpath/internal/token/ArrayPathToken.java

@ -54,10 +54,18 @@ public class ArrayPathToken extends PathToken {
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if(model == null){
throw new PathNotFoundException("The path " + currentPath + " is null");
if (! isUpstreamDefinite()) {
return;
} else {
throw new PathNotFoundException("The path " + currentPath + " is null");
}
}
if (!ctx.jsonProvider().isArray(model)) {
throw new InvalidPathException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model));
if (! isUpstreamDefinite()) {
return;
} else {
throw new InvalidPathException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model));
}
}
try {
@ -143,7 +151,9 @@ public class ArrayPathToken extends PathToken {
break;
}
} catch (IndexOutOfBoundsException e) {
throw new PathNotFoundException("Index out of bounds when evaluating path " + currentPath);
if (isUpstreamDefinite()) {
throw new PathNotFoundException("Index out of bounds when evaluating path " + currentPath);
}
}
}

4
json-path/src/main/java/com/jayway/jsonpath/internal/token/PredicatePathToken.java

@ -69,7 +69,9 @@ public class PredicatePathToken extends PathToken {
idx++;
}
} else {
throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model));
if (isUpstreamDefinite()) {
throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model));
}
}
}

6
json-path/src/main/java/com/jayway/jsonpath/internal/token/PropertyPathToken.java

@ -38,7 +38,11 @@ public class PropertyPathToken extends PathToken {
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (!ctx.jsonProvider().isMap(model)) {
throw new PathNotFoundException("Property " + getPathFragment() + " not found in path " + currentPath);
if (! isUpstreamDefinite()) {
return;
} else {
throw new PathNotFoundException("Property " + getPathFragment() + " not found in path " + currentPath);
}
}
handleObjectProperty(currentPath, model, ctx, properties);

6
json-path/src/main/java/com/jayway/jsonpath/internal/token/ScanPathToken.java

@ -14,6 +14,7 @@
*/
package com.jayway.jsonpath.internal.token;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.spi.json.JsonProvider;
@ -167,6 +168,11 @@ public class ScanPathToken extends PathToken {
@Override
public boolean matches(Object model) {
if (ctx.jsonProvider().isMap(model)) {
if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
// Have to require properties defined in path when an indefinite path is evaluated,
// so have to go there and search for it.
return true;
}
Collection<String> keys = ctx.jsonProvider().getPropertyKeys(model);
return keys.containsAll(propertyPathToken.getProperties());
}

88
json-path/src/test/java/com/jayway/jsonpath/DeepScanTest.java

@ -0,0 +1,88 @@
package com.jayway.jsonpath;
import org.junit.Test;
import static com.jayway.jsonpath.TestUtils.assertEvaluationThrows;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Deep scan is indefinite, so certain "illegal" actions become a no-op instead of a path evaluation exception.
*/
public class DeepScanTest extends BaseTest {
@Test
public void when_deep_scanning_non_array_subscription_is_ignored() {
Object result = JsonPath.parse("{\"x\": [0,1,[0,1,2,3,null],null]}").read("$..[2][3]");
assertThat(result).asList().containsOnly(3);
result = JsonPath.parse("{\"x\": [0,1,[0,1,2,3,null],null], \"y\": [0,1,2]}").read("$..[2][3]");
assertThat(result).asList().containsOnly(3);
result = JsonPath.parse("{\"x\": [0,1,[0,1,2],null], \"y\": [0,1,2]}").read("$..[2][3]");
assertThat(result).asList().isEmpty();
}
@Test
public void when_deep_scanning_null_subscription_is_ignored() {
Object result = JsonPath.parse("{\"x\": [null,null,[0,1,2,3,null],null]}").read("$..[2][3]");
assertThat(result).asList().containsOnly(3);
result = JsonPath.parse("{\"x\": [null,null,[0,1,2,3,null],null], \"y\": [0,1,null]}").read("$..[2][3]");
assertThat(result).asList().containsOnly(3);
}
@Test
public void when_deep_scanning_array_index_oob_is_ignored() {
Object result = JsonPath.parse("{\"x\": [0,1,[0,1,2,3,10],null]}").read("$..[4]");
assertThat(result).asList().containsOnly(10);
result = JsonPath.parse("{\"x\": [null,null,[0,1,2,3]], \"y\": [null,null,[0,1]]}").read("$..[2][3]");
assertThat(result).asList().containsOnly(3);
}
@Test
public void definite_upstream_illegal_array_access_throws() {
assertEvaluationThrows("{\"foo\": {\"bar\": null}}", "$.foo.bar.[5]", PathNotFoundException.class);
assertEvaluationThrows("{\"foo\": {\"bar\": null}}", "$.foo.bar.[5, 10]", PathNotFoundException.class);
assertEvaluationThrows("{\"foo\": {\"bar\": 4}}", "$.foo.bar.[5]", InvalidPathException.class);
assertEvaluationThrows("{\"foo\": {\"bar\": 4}}", "$.foo.bar.[5, 10]", InvalidPathException.class);
assertEvaluationThrows("{\"foo\": {\"bar\": []}}", "$.foo.bar.[5]", PathNotFoundException.class);
}
@Test
public void when_deep_scanning_illegal_property_access_is_ignored() {
Object result = JsonPath.parse("{\"x\": {\"foo\": {\"bar\": 4}}, \"y\": {\"foo\": 1}}").read("$..foo");
assertThat(result).asList().hasSize(2);
result = JsonPath.parse("{\"x\": {\"foo\": {\"bar\": 4}}, \"y\": {\"foo\": 1}}").read("$..foo.bar");
assertThat(result).asList().containsOnly(4);
result = JsonPath.parse("{\"x\": {\"foo\": {\"bar\": 4}}, \"y\": {\"foo\": 1}}").read("$..[*].foo.bar");
assertThat(result).asList().containsOnly(4);
result = JsonPath.parse("{\"x\": {\"foo\": {\"baz\": 4}}, \"y\": {\"foo\": 1}}").read("$..[*].foo.bar");
assertThat(result).asList().isEmpty();
}
@Test
public void when_deep_scanning_illegal_predicate_is_ignored() {
Object result = JsonPath.parse("{\"x\": {\"foo\": {\"bar\": 4}}, \"y\": {\"foo\": 1}}").read(
"$..foo[?(@.bar)].bar");
assertThat(result).asList().containsOnly(4);
result = JsonPath.parse("{\"x\": {\"foo\": {\"bar\": 4}}, \"y\": {\"foo\": 1}}").read(
"$..[*]foo[?(@.bar)].bar");
assertThat(result).asList().containsOnly(4);
}
@Test
public void when_deep_scanning_require_properties_still_counts() {
final Configuration conf = Configuration.defaultConfiguration().addOptions(Option.REQUIRE_PROPERTIES);
Object result = JsonPath.parse("[{\"x\": {\"foo\": {\"x\": 4}, \"x\": null}, \"y\": {\"x\": 1}}, {\"x\": []}]").read(
"$..x");
assertThat(result).asList().hasSize(5);
// foo.bar must be found in every object node after deep scan (which is impossible)
assertEvaluationThrows("{\"foo\": {\"bar\": 4}}", "$..foo.bar", PathNotFoundException.class, conf);
}
}

32
json-path/src/test/java/com/jayway/jsonpath/TestUtils.java

@ -0,0 +1,32 @@
package com.jayway.jsonpath;
import static com.jayway.jsonpath.JsonPath.using;
import static org.assertj.core.api.Assertions.fail;
public final class TestUtils {
private TestUtils() {}
public static void assertEvaluationThrows(final String json, final String path,
Class<? extends JsonPathException> expected) {
assertEvaluationThrows(json, path, expected, Configuration.defaultConfiguration());
}
/**
* Shortcut for expected exception testing during path evaluation.
*
* @param conf conf to use during evaluation
* @param json json to parse
* @param path jsonpath do evaluate
* @param expected expected exception class (reference comparison, not an instanceof)
*/
public static void assertEvaluationThrows(final String json, final String path,
Class<? extends JsonPathException> expected, final Configuration conf) {
try {
using(conf).parse(json).read(path);
fail("Should throw " + expected.getName());
} catch (JsonPathException exc) {
if (exc.getClass() != expected)
throw exc;
}
}
}
Loading…
Cancel
Save