Browse Source

Merge pull request #1 from json-path/master

Update from json-path/JsonPath
pull/538/head
MarcP04 5 years ago committed by GitHub
parent
commit
648e1bfde9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 52
      README.md
  3. 29
      build.gradle
  4. 10
      json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java
  5. 2
      json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java
  6. 6
      json-path-assert/src/test/java/com/jayway/jsonpath/matchers/HasNoJsonPathTest.java
  7. 11
      json-path/src/main/java/com/jayway/jsonpath/Configuration.java
  8. 66
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  9. 2
      json-path/src/main/java/com/jayway/jsonpath/Filter.java
  10. 10
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  11. 11
      json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java
  12. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java
  13. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java
  14. 8
      json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java
  15. 8
      json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java
  16. 88
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java
  17. 40
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java
  18. 48
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java
  19. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java
  20. 652
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java
  21. 661
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java
  22. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunction.java
  23. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/JsonLateBindingValue.java
  24. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/PathLateBindingValue.java
  25. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java
  26. 52
      json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java
  27. 138
      json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java
  28. 18
      json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java
  29. 114
      json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java
  30. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java
  31. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/path/FunctionPathToken.java
  32. 15
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java
  33. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java
  34. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java
  35. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java
  36. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java
  37. 5
      json-path/src/main/java/com/jayway/jsonpath/spi/cache/Cache.java
  38. 4
      json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java
  39. 3
      json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java
  40. 2
      json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java
  41. 6
      json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java
  42. 20
      json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java
  43. 52
      json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
  44. 35
      json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java
  45. 3
      json-path/src/test/java/com/jayway/jsonpath/MultiPropTest.java
  46. 24
      json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java
  47. 52
      json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java
  48. 8
      json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java
  49. 47
      json-path/src/test/java/com/jayway/jsonpath/internal/filter/PatternFlagTest.java
  50. 33
      json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java
  51. 4
      json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue234.java
  52. 6
      json-path/src/test/java/com/jayway/jsonpath/internal/function/NestedFunctionTest.java
  53. 2
      json-path/src/test/java/com/jayway/jsonpath/old/ArraySlicingTest.java
  54. 6
      json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java
  55. 18
      json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java
  56. 7
      json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java
  57. 4
      json-path/src/test/java/com/jayway/jsonpath/old/NullHandlingTest.java
  58. 2
      json-path/src/test/java/com/jayway/jsonpath/old/internal/PropertyPathTokenTest.java
  59. 2
      system.properties

1
.gitignore vendored

@ -13,3 +13,4 @@ TODO
gradle.properties
build
bin/
out/

52
README.md

@ -5,7 +5,7 @@ Jayway JsonPath
[![Build Status](https://travis-ci.org/json-path/JsonPath.svg?branch=master)](https://travis-ci.org/json-path/JsonPath)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.jayway.jsonpath/json-path/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.jayway.jsonpath/json-path)
[![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.jayway.jsonpath/json-path/badge.svg)](http://www.javadoc.io/doc/com.jayway.jsonpath/json-path)
[![Javadoc](https://www.javadoc.io/badge/com.jayway.jsonpath/json-path.svg)](http://www.javadoc.io/doc/com.jayway.jsonpath/json-path)
Jayway JsonPath is a Java port of [Stefan Goessner JsonPath implementation](http://goessner.net/articles/JsonPath/).
@ -77,13 +77,13 @@ Functions
Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression.
The function output is dictated by the function itself.
| Function | Description | Output |
| :------------------------ | :------------------------------------------------------------------ |-----------|
| min() | Provides the min value of an array of numbers | Double |
| max() | Provides the max value of an array of numbers | Double |
| avg() | Provides the average value of an array of numbers | Double |
| stddev() | Provides the standard deviation value of an array of numbers | Double |
| length() | Provides the length of an array | Integer |
| Function | Description | Output |
| :------------------------ | :----------------------------------------------------------------- |-----------|
| min() | Provides the min value of an array of numbers | Double |
| max() | Provides the max value of an array of numbers | Double |
| avg() | Provides the average value of an array of numbers | Double |
| stddev() | Provides the standard deviation value of an array of numbers | Double |
| length() | Provides the length of an array | Integer |
Filter Operators
@ -91,22 +91,20 @@ Filter Operators
Filters are logical expressions used to filter arrays. A typical filter would be `[?(@.age > 18)]` where `@` represents the current item being processed. More complex filters can be created with logical operators `&&` and `||`. String literals must be enclosed by single or double quotes (`[?(@.color == 'blue')]` or `[?(@.color == "blue")]`).
| Operator | Description |
| :----------------------- | :-------------------------------------------------------------------- |
| == | left is equal to right (note that 1 is not equal to '1') |
| != | left is not equal to right |
| < | left is less than right |
| <= | left is less or equal to right |
| > | left is greater than right |
| >= | left is greater than or equal to right |
| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] |
| in | left exists in right [?(@.size in ['S', 'M'])] |
| nin | left does not exists in right |
| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
| anyof | left has an intersection with right [?(@.sizes anyof ['M', 'L'])] |
| noneof | left has no intersection with right [?(@.sizes noneof ['M', 'L'])] |
| size | size of left (array or string) should match right |
| empty | left (array or string) should be empty |
| Operator | Description |
| :----------------------- | :---------------------------------------------------------------- |
| == | left is equal to right (note that 1 is not equal to '1') |
| != | left is not equal to right |
| < | left is less than right |
| <= | left is less or equal to right |
| > | left is greater than right |
| >= | left is greater than or equal to right |
| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] |
| in | left exists in right [?(@.size in ['S', 'M'])] |
| nin | left does not exists in right |
| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
| size | size of left (array or string) should match right |
| empty | left (array or string) should be empty |
Path Examples
@ -241,7 +239,7 @@ String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
```
If you configure JsonPath to use `JacksonMappingProvider` or GsonMappingProvider` you can even map your JsonPath output directly into POJO's.
If you configure JsonPath to use `JacksonMappingProvider` or `GsonMappingProvider` you can even map your JsonPath output directly into POJO's.
```java
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
@ -324,7 +322,7 @@ List<Map<String, Object>> books =
Path vs Value
-------------
In the Goessner implementation a JsonPath can return either `Path` or `Value`. `Value` is the default and what all the examples above are returning. If you rather have the path of the elements our query is hitting this can be acheived with an option.
In the Goessner implementation a JsonPath can return either `Path` or `Value`. `Value` is the default and what all the examples above are returning. If you rather have the path of the elements our query is hitting this can be achieved with an option.
```java
Configuration conf = Configuration.builder()
@ -400,7 +398,7 @@ This option makes sure no exceptions are propagated from path evaluation. It fol
### JsonProvider SPI
JsonPath is shipped with three different JsonProviders:
JsonPath is shipped with five different JsonProviders:
* [JsonSmartJsonProvider](https://code.google.com/p/json-smart/) (default)
* [JacksonJsonProvider](https://github.com/FasterXML/jackson)

29
build.gradle

@ -4,24 +4,31 @@ buildscript {
}
dependencies {
classpath 'me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1'
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
classpath 'me.champeau.gradle:japicmp-gradle-plugin:0.1.0'
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
}
}
ext {
libs = [
slf4jApi: 'org.slf4j:slf4j-api:1.7.25',
jsonSmart: 'net.minidev:json-smart:2.3',
jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.6.3',
gson: 'com.google.code.gson:gson:2.3.1',
jettison: 'org.codehaus.jettison:jettison:1.3.7',
jsonOrg: 'org.json:json:20140107',
tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.3',
gson: 'com.google.code.gson:gson:2.8.5',
hamcrestCore: 'org.hamcrest:hamcrest-core:1.3',
hamcrestLibrary: 'org.hamcrest:hamcrest-library:1.3',
jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.9.8',
jettison: 'org.codehaus.jettison:jettison:1.3.7',
jsonOrg: 'org.json:json:20140107',
jsonSmart: 'net.minidev:json-smart:2.3',
slf4jApi: 'org.slf4j:slf4j-api:1.7.26',
tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.4',
test: ['org.slf4j:slf4j-simple:1.7.16', 'org.assertj:assertj-core:2.1.0', 'commons-io:commons-io:2.4','org.hamcrest:hamcrest-core:1.3', 'org.hamcrest:hamcrest-library:1.3', 'junit:junit:4.12']
test: [
'commons-io:commons-io:2.4',
'junit:junit:4.12',
'org.assertj:assertj-core:3.11.1',
'org.hamcrest:hamcrest-core:1.3',
'org.hamcrest:hamcrest-library:1.3',
'org.slf4j:slf4j-simple:1.7.26'
]
]
snapshotVersion = false
}
@ -45,8 +52,8 @@ subprojects {
apply plugin: 'signing'
apply plugin: 'osgi'
sourceCompatibility = 1.6
targetCompatibility = 1.6
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()

10
json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java

@ -22,11 +22,11 @@ public interface JsonAsserter {
<T> JsonAsserter assertThat(String path, Matcher<T> matcher);
/**
* @param path
* @param matcher
* @param message
* @param <T>
* @return
* @param path the json path specifying the value being compared
* @param matcher an expression, built of Matchers, specifying allowed values
* @param message the explanation message
* @param <T> the static type that should be returned by the path
* @return this to allow fluent assertion chains
*/
<T> JsonAsserter assertThat(String path, Matcher<T> matcher, String message);

2
json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java

@ -38,7 +38,7 @@ public class WithJsonPath<T> extends TypeSafeMatcher<ReadContext> {
@Override
protected void describeMismatchSafely(ReadContext context, Description mismatchDescription) {
try {
T value = jsonPath.read(context.json());
T value = jsonPath.read(context.jsonString());
mismatchDescription
.appendText("json path ")
.appendValue(jsonPath.getPath())

6
json-path-assert/src/test/java/com/jayway/jsonpath/matchers/HasNoJsonPathTest.java

@ -8,6 +8,7 @@ import static org.junit.Assert.assertThat;
public class HasNoJsonPathTest {
private static final String JSON_STRING = "{" +
"\"none\": null," +
"\"name\": \"Jessie\"" +
"}";
@ -21,6 +22,11 @@ public class HasNoJsonPathTest {
assertThat(JSON_STRING, not(hasNoJsonPath("$.name")));
}
@Test
public void shouldNotMatchExplicitNull() {
assertThat(JSON_STRING, not(hasNoJsonPath("$.none")));
}
@Test
public void shouldBeDescriptive() {
assertThat(hasNoJsonPath("$.name"),

11
json-path/src/main/java/com/jayway/jsonpath/Configuration.java

@ -18,12 +18,7 @@ import com.jayway.jsonpath.internal.DefaultsImpl;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.*;
import static com.jayway.jsonpath.internal.Utils.notNull;
import static java.util.Arrays.asList;
@ -142,7 +137,7 @@ public class Configuration {
/**
* Creates a new configuration with the provided options. Options in this configuration are discarded.
* @param options
* @return
* @return the new configuration instance
*/
public Configuration setOptions(Option... options) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
@ -150,7 +145,7 @@ public class Configuration {
/**
* Returns the options used by this configuration
* @return
* @return the new configuration instance
*/
public Set<Option> getOptions() {
return options;

66
json-path/src/main/java/com/jayway/jsonpath/Criteria.java

@ -28,7 +28,9 @@ import java.util.List;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.Utils.notNull;
import com.jayway.jsonpath.internal.filter.ValueNodes;
import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
import static com.jayway.jsonpath.internal.filter.ValueNodes.PredicateNode;
/**
*
@ -225,7 +227,7 @@ public class Criteria implements Predicate {
public Criteria in(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.IN;
this.right = new ValueNode.ValueListNode(c);
this.right = new ValueListNode(c);
return this;
}
@ -263,7 +265,7 @@ public class Criteria implements Predicate {
public Criteria nin(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.NIN;
this.right = new ValueNode.ValueListNode(c);
this.right = new ValueListNode(c);
return this;
}
@ -290,57 +292,7 @@ public class Criteria implements Predicate {
public Criteria subsetof(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.SUBSETOF;
this.right = new ValueNode.ValueListNode(c);
return this;
}
/**
* The <code>anyof</code> operator selects objects for which the specified field is
* an array that contain at least an element in the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria anyof(Object... o) {
return subsetof(Arrays.asList(o));
}
/**
* The <code>anyof</code> operator selects objects for which the specified field is
* an array that contain at least an element in the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria anyof(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.ANYOF;
this.right = new ValueNode.ValueListNode(c);
return this;
}
/**
* The <code>noneof</code> operator selects objects for which the specified field is
* an array that does not contain any of the elements of the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria noneof(Object... o) {
return subsetof(Arrays.asList(o));
}
/**
* The <code>noneof</code> operator selects objects for which the specified field is
* an array that does not contain any of the elements of the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria noneof(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.NONEOF;
this.right = new ValueNode.ValueListNode(c);
this.right = new ValueListNode(c);
return this;
}
@ -365,7 +317,7 @@ public class Criteria implements Predicate {
public Criteria all(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.ALL;
this.right = new ValueNode.ValueListNode(c);
this.right = new ValueListNode(c);
return this;
}
@ -439,7 +391,7 @@ public class Criteria implements Predicate {
*/
public Criteria empty(boolean empty) {
this.criteriaType = RelationalOperator.EMPTY;
this.right = empty ? ValueNode.TRUE : ValueNode.FALSE;
this.right = empty ? ValueNodes.TRUE : ValueNodes.FALSE;
return this;
}
@ -451,7 +403,7 @@ public class Criteria implements Predicate {
*/
public Criteria matches(Predicate p) {
this.criteriaType = RelationalOperator.MATCHES;
this.right = new ValueNode.PredicateNode(p);
this.right = new PredicateNode(p);
return this;
}

2
json-path/src/main/java/com/jayway/jsonpath/Filter.java

@ -39,7 +39,7 @@ public abstract class Filter implements Predicate {
/**
* Create a new Filter based on given list of criteria.
* @param predicates list of criteria all needs to evaluate to true
* @return
* @return the filter
*/
public static Filter filter(Collection<Predicate> predicates) {
return new AndFilter(predicates);

10
json-path/src/main/java/com/jayway/jsonpath/JsonPath.java

@ -15,11 +15,7 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.ParseContextImpl;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.*;
import com.jayway.jsonpath.internal.path.PathCompiler;
import com.jayway.jsonpath.spi.json.JsonProvider;
@ -289,7 +285,7 @@ public class JsonPath {
* Adds or updates the Object this path points to in the provided jsonObject with a key with a value
*
* @param jsonObject a json object
* @param value the key to add or update
* @param key the key to add or update
* @param value the new value
* @param configuration configuration to use
* @param <T> expected return type
@ -628,6 +624,7 @@ public class JsonPath {
* @param json url
* @return a read context
*/
@Deprecated
public static DocumentContext parse(URL json) throws IOException {
return new ParseContextImpl().parse(json);
}
@ -683,6 +680,7 @@ public class JsonPath {
* @param json input
* @return a read context
*/
@Deprecated
public static DocumentContext parse(URL json, Configuration configuration) throws IOException {
return new ParseContextImpl(configuration).parse(json);
}

11
json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java

@ -172,7 +172,7 @@ public class CharacterIndex {
inEscape = false;
} else if('\\' == charAt(readPosition)){
inEscape = true;
} else if (c == charAt(readPosition) && !inEscape){
} else if (c == charAt(readPosition)){
return readPosition;
}
readPosition ++;
@ -220,16 +220,17 @@ public class CharacterIndex {
incrementPosition(1);
}
public void readSignificantSubSequence(CharSequence s) {
public boolean hasSignificantSubSequence(CharSequence s) {
skipBlanks();
if (! inBounds(position + s.length() - 1)) {
throw new InvalidPathException(String.format("End of string reached while expecting: %s", s));
return false;
}
if (! subSequence(position, position + s.length()).equals(s)) {
throw new InvalidPathException(String.format("Expected: %s", s));
return false;
}
incrementPosition(s.length());
return true;
}
public int indexOfPreviousSignificantChar(int startPosition){
@ -314,4 +315,4 @@ public class CharacterIndex {
skipBlanksAtEnd();
return this;
}
}
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java

@ -32,6 +32,6 @@ public final class DefaultsImpl implements Defaults {
}
private DefaultsImpl() {
};
}
}

3
json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java

@ -46,7 +46,7 @@ public class ParseContextImpl implements ParseContext {
@Override
public DocumentContext parse(InputStream json, String charset) {
notNull(json, "json input stream can not be null");
notNull(json, "charset can not be null");
notNull(charset, "charset can not be null");
try {
Object obj = configuration.jsonProvider().parse(json, charset);
return new JsonContext(obj, configuration);
@ -68,6 +68,7 @@ public class ParseContextImpl implements ParseContext {
}
@Override
@Deprecated
public DocumentContext parse(URL url) throws IOException {
notNull(url, "url can not be null");
InputStream fis = null;

8
json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java

@ -107,7 +107,7 @@ public abstract class PathRef implements Comparable<PathRef> {
@Override
public void set(Object newVal, Configuration configuration) {
throw new InvalidModificationException("Invalid delete operation");
throw new InvalidModificationException("Invalid set operation");
}
public void convert(MapFunction mapFunction, Configuration configuration){
@ -212,7 +212,7 @@ public abstract class PathRef implements Comparable<PathRef> {
public int compareTo(PathRef o) {
if(o instanceof ArrayIndexPathRef){
ArrayIndexPathRef pf = (ArrayIndexPathRef) o;
return Integer.valueOf(pf.index).compareTo(this.index);
return Integer.compare(pf.index, this.index);
}
return super.compareTo(o);
}
@ -298,7 +298,9 @@ public abstract class PathRef implements Comparable<PathRef> {
public void convert(MapFunction mapFunction, Configuration configuration) {
for (String property : properties) {
Object currentValue = configuration.jsonProvider().getMapValue(parent, property);
configuration.jsonProvider().setProperty(parent, property, mapFunction.map(currentValue, configuration));
if (currentValue != JsonProvider.UNDEFINED) {
configuration.jsonProvider().setProperty(parent, property, mapFunction.map(currentValue, configuration));
}
}
}

8
json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java

@ -24,8 +24,8 @@ import java.util.Iterator;
public final class Utils {
// accept a collection of objects, since all objects have toString()
public static String join(String delimiter, String wrap, Iterable<? extends Object> objs) {
Iterator<? extends Object> iter = objs.iterator();
public static String join(String delimiter, String wrap, Iterable<?> objs) {
Iterator<?> iter = objs.iterator();
if (!iter.hasNext()) {
return "";
}
@ -38,7 +38,7 @@ public final class Utils {
}
// accept a collection of objects, since all objects have toString()
public static String join(String delimiter, Iterable<? extends Object> objs) {
public static String join(String delimiter, Iterable<?> objs) {
return join(delimiter, "", objs);
}
@ -175,7 +175,7 @@ public final class Utils {
}
int len = str.length();
StringWriter writer = new StringWriter(len);
StringBuffer unicode = new StringBuffer(4);
StringBuilder unicode = new StringBuilder(4);
boolean hadSlash = false;
boolean inUnicode = false;
for (int i = 0; i < len; i++) {

88
json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java

@ -2,6 +2,7 @@ package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Predicate;
import static com.jayway.jsonpath.internal.filter.ValueNodes.*;
import java.util.HashMap;
import java.util.Map;
@ -30,8 +31,6 @@ public class EvaluatorFactory {
evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator());
evaluators.put(RelationalOperator.TYPE, new TypeEvaluator());
evaluators.put(RelationalOperator.SUBSETOF, new SubsetOfEvaluator());
evaluators.put(RelationalOperator.ANYOF, new AnyOfEvaluator());
evaluators.put(RelationalOperator.NONEOF, new NoneOfEvaluator());
}
public static Evaluator createEvaluator(RelationalOperator operator){
@ -170,7 +169,7 @@ public class EvaluatorFactory {
private static class InEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueNode.ValueListNode valueListNode;
ValueListNode valueListNode;
if(right.isJsonNode()){
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
@ -195,12 +194,12 @@ public class EvaluatorFactory {
private static class AllEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueNode.ValueListNode requiredValues = right.asValueListNode();
ValueListNode requiredValues = right.asValueListNode();
if(left.isJsonNode()){
ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); //returns UndefinedNode if conversion is not possible
if(valueNode.isValueListNode()){
ValueNode.ValueListNode shouldContainAll = valueNode.asValueListNode();
ValueListNode shouldContainAll = valueNode.asValueListNode();
for (ValueNode required : requiredValues) {
if(!shouldContainAll.contains(required)){
return false;
@ -251,7 +250,7 @@ public class EvaluatorFactory {
}
}
private boolean matches(ValueNode.PatternNode patternNode, String inputToMatch) {
private boolean matches(PatternNode patternNode, String inputToMatch) {
return patternNode.getCompiledPattern().matcher(inputToMatch).matches();
}
@ -271,7 +270,7 @@ public class EvaluatorFactory {
private static class SubsetOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueNode.ValueListNode rightValueListNode;
ValueListNode rightValueListNode;
if(right.isJsonNode()){
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
@ -282,7 +281,7 @@ public class EvaluatorFactory {
} else {
rightValueListNode = right.asValueListNode();
}
ValueNode.ValueListNode leftValueListNode;
ValueListNode leftValueListNode;
if(left.isJsonNode()){
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
@ -297,77 +296,4 @@ public class EvaluatorFactory {
}
}
private static class AnyOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueNode.ValueListNode rightValueListNode;
if (right.isJsonNode()) {
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
rightValueListNode = vn.asValueListNode();
}
} else {
rightValueListNode = right.asValueListNode();
}
ValueNode.ValueListNode leftValueListNode;
if (left.isJsonNode()) {
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
leftValueListNode = vn.asValueListNode();
}
} else {
leftValueListNode = left.asValueListNode();
}
for (ValueNode leftValueNode : leftValueListNode) {
for (ValueNode rightValueNode : rightValueListNode) {
if (leftValueNode.equals(rightValueNode)) {
return true;
}
}
}
return false;
}
}
private static class NoneOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueNode.ValueListNode rightValueListNode;
if (right.isJsonNode()) {
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
rightValueListNode = vn.asValueListNode();
}
} else {
rightValueListNode = right.asValueListNode();
}
ValueNode.ValueListNode leftValueListNode;
if (left.isJsonNode()) {
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
leftValueListNode = vn.asValueListNode();
}
} else {
leftValueListNode = left.asValueListNode();
}
for (ValueNode leftValueNode : leftValueListNode) {
for (ValueNode rightValueNode : rightValueListNode) {
if (leftValueNode.equals(rightValueNode)) {
return false;
}
}
}
return true;
}
}
}

40
json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java

@ -4,13 +4,14 @@ import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.CharacterIndex;
import static com.jayway.jsonpath.internal.filter.ValueNodes.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class FilterCompiler {
public class FilterCompiler {
private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class);
private static final char DOC_CONTEXT = '$';
@ -132,11 +133,9 @@ public class FilterCompiler {
while (true) {
int savepoint = filter.position();
try {
filter.readSignificantSubSequence(LogicalOperator.OR.getOperatorString());
if (filter.hasSignificantSubSequence(LogicalOperator.OR.getOperatorString())) {
ops.add(readLogicalAND());
}
catch (InvalidPathException exc) {
} else {
filter.setPosition(savepoint);
break;
}
@ -152,11 +151,9 @@ public class FilterCompiler {
while (true) {
int savepoint = filter.position();
try {
filter.readSignificantSubSequence(LogicalOperator.AND.getOperatorString());
if (filter.hasSignificantSubSequence(LogicalOperator.AND.getOperatorString())) {
ops.add(readLogicalANDOperand());
}
catch (InvalidPathException exc) {
} else {
filter.setPosition(savepoint);
break;
}
@ -201,10 +198,10 @@ public class FilterCompiler {
filter.setPosition(savepoint);
}
ValueNode.PathNode pathNode = left.asPathNode();
PathNode pathNode = left.asPathNode();
left = pathNode.asExistsCheck(pathNode.shouldExists());
RelationalOperator operator = RelationalOperator.EXISTS;
ValueNode right = left.asPathNode().shouldExists() ? ValueNode.TRUE : ValueNode.FALSE;
ValueNode right = left.asPathNode().shouldExists() ? ValueNodes.TRUE : ValueNodes.FALSE;
return new RelationalExpressionNode(left, operator, right);
}
@ -243,7 +240,7 @@ public class FilterCompiler {
return RelationalOperator.fromString(operator.toString());
}
private ValueNode.NullNode readNullLiteral() {
private NullNode readNullLiteral() {
int begin = filter.position();
if(filter.currentChar() == NULL && filter.inBounds(filter.position() + 3)){
CharSequence nullValue = filter.subSequence(filter.position(), filter.position() + 4);
@ -256,7 +253,7 @@ public class FilterCompiler {
throw new InvalidPathException("Expected <null> value");
}
private ValueNode.JsonNode readJsonLiteral(){
private JsonNode readJsonLiteral(){
int begin = filter.position();
char openChar = filter.currentChar();
@ -277,14 +274,17 @@ public class FilterCompiler {
}
private ValueNode.PatternNode readPattern() {
private PatternNode readPattern() {
int begin = filter.position();
int closingIndex = filter.nextIndexOfUnescaped(PATTERN);
if (closingIndex == -1) {
throw new InvalidPathException("Pattern not closed. Expected " + PATTERN + " in " + filter);
} else {
if(filter.inBounds(closingIndex+1) && filter.charAt(closingIndex+1) == IGNORE_CASE){
closingIndex++;
if(filter.inBounds(closingIndex+1)) {
int equalSignIndex = filter.nextIndexOf('=');
int endIndex = equalSignIndex > closingIndex ? equalSignIndex : filter.nextIndexOfUnescaped(CLOSE_PARENTHESIS);
CharSequence flags = filter.subSequence(closingIndex + 1, endIndex);
closingIndex += flags.length();
}
filter.setPosition(closingIndex + 1);
}
@ -293,7 +293,7 @@ public class FilterCompiler {
return ValueNode.createPatternNode(pattern);
}
private ValueNode.StringNode readStringLiteral(char endChar) {
private StringNode readStringLiteral(char endChar) {
int begin = filter.position();
int closingSingleQuoteIndex = filter.nextIndexOfUnescaped(endChar);
@ -307,7 +307,7 @@ public class FilterCompiler {
return ValueNode.createStringNode(stringLiteral, true);
}
private ValueNode.NumberNode readNumberLiteral() {
private NumberNode readNumberLiteral() {
int begin = filter.position();
while (filter.inBounds() && filter.isNumberCharacter(filter.position())) {
@ -318,7 +318,7 @@ public class FilterCompiler {
return ValueNode.createNumberNode(numberLiteral);
}
private ValueNode.BooleanNode readBooleanLiteral() {
private BooleanNode readBooleanLiteral() {
int begin = filter.position();
int end = filter.currentChar() == TRUE ? filter.position() + 3 : filter.position() + 4;
@ -335,7 +335,7 @@ public class FilterCompiler {
return ValueNode.createBooleanNode(boolValue);
}
private ValueNode.PathNode readPath() {
private PathNode readPath() {
char previousSignificantChar = filter.previousSignificantChar();
int begin = filter.position();

48
json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java

@ -0,0 +1,48 @@
package com.jayway.jsonpath.internal.filter;
import java.util.regex.Pattern;
public enum PatternFlag {
UNIX_LINES(Pattern.UNIX_LINES, 'd'),
CASE_INSENSITIVE(Pattern.CASE_INSENSITIVE, 'i'),
COMMENTS(Pattern.COMMENTS, 'x'),
MULTILINE(Pattern.MULTILINE, 'm'),
DOTALL(Pattern.DOTALL, 's'),
UNICODE_CASE(Pattern.UNICODE_CASE, 'u'),
UNICODE_CHARACTER_CLASS(Pattern.UNICODE_CHARACTER_CLASS, 'U');
private final int code;
private final char flag;
private PatternFlag(int code, char flag) {
this.code = code;
this.flag = flag;
}
public static int parseFlags(char[] flags) {
int flagsValue = 0;
for (char flag : flags) {
flagsValue |= getCodeByFlag(flag);
}
return flagsValue;
}
public static String parseFlags(int flags) {
StringBuilder builder = new StringBuilder();
for (PatternFlag patternFlag : PatternFlag.values()) {
if ((patternFlag.code & flags) == patternFlag.code) {
builder.append(patternFlag.flag);
}
}
return builder.toString();
}
private static int getCodeByFlag(char flag) {
for (PatternFlag patternFlag : PatternFlag.values()) {
if (patternFlag.flag == flag) {
return patternFlag.code;
}
}
return 0;
}
}

4
json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java

@ -30,9 +30,7 @@ public enum RelationalOperator {
TYPE("TYPE"),
MATCHES("MATCHES"),
EMPTY("EMPTY"),
SUBSETOF("SUBSETOF"),
ANYOF("ANYOF"),
NONEOF("NONEOF");
SUBSETOF("SUBSETOF");
private final String operatorString;

652
json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java

@ -1,40 +1,17 @@
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.Configuration;
import java.util.regex.Pattern;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.path.PathCompiler;
import com.jayway.jsonpath.internal.path.PredicateContextImpl;
import com.jayway.jsonpath.spi.json.JsonProvider;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.filter.ValueNodes.*;
public abstract class ValueNode {
public static final NullNode NULL_NODE = new NullNode();
public static final BooleanNode TRUE = new BooleanNode("true");
public static final BooleanNode FALSE = new BooleanNode("false");
public static final UndefinedNode UNDEFINED = new UndefinedNode();
public abstract Class<?> type(Predicate.PredicateContext ctx);
public boolean isPatternNode() {
@ -174,7 +151,7 @@ public abstract class ValueNode {
//
//----------------------------------------------------
public static ValueNode toValueNode(Object o){
if(o == null) return ValueNode.NULL_NODE;
if(o == null) return NULL_NODE;
if(o instanceof ValueNode) return (ValueNode)o;
if(o instanceof Class) return createClassNode((Class)o);
else if(isPath(o)) return new PathNode(o.toString(), false, false);
@ -235,624 +212,5 @@ public abstract class ValueNode {
return new PathNode(path);
}
//----------------------------------------------------
//
// ValueNode Implementations
//
//----------------------------------------------------
public static class PatternNode extends ValueNode {
private final String pattern;
private final Pattern compiledPattern;
private PatternNode(CharSequence charSequence) {
String tmp = charSequence.toString();
int begin = tmp.indexOf('/');
int end = tmp.lastIndexOf('/');
int flags = tmp.endsWith("/i") ? Pattern.CASE_INSENSITIVE : 0;
this.pattern = tmp.substring(begin + 1, end);
this.compiledPattern = Pattern.compile(pattern, flags);
}
public PatternNode(Pattern pattern) {
this.pattern = pattern.pattern();
this.compiledPattern = pattern;
}
public Pattern getCompiledPattern() {
return compiledPattern;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.TYPE;
}
public boolean isPatternNode() {
return true;
}
public PatternNode asPatternNode() {
return this;
}
@Override
public String toString() {
String flags = "";
if((compiledPattern.flags() & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE){
flags = "i";
}
if(!pattern.startsWith("/")){
return "/" + pattern + "/" + flags;
} else {
return pattern;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PatternNode)) return false;
PatternNode that = (PatternNode) o;
return !(compiledPattern != null ? !compiledPattern.equals(that.compiledPattern) : that.compiledPattern != null);
}
}
public static class JsonNode extends ValueNode {
private final Object json;
private final boolean parsed;
private JsonNode(CharSequence charSequence) {
json = charSequence.toString();
parsed = false;
}
public JsonNode(Object parsedJson) {
json = parsedJson;
parsed = true;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
if(isArray(ctx)) return List.class;
else if(isMap(ctx)) return Map.class;
else if(parse(ctx) instanceof Number) return Number.class;
else if(parse(ctx) instanceof String) return String.class;
else if(parse(ctx) instanceof Boolean) return Boolean.class;
else return Void.class;
}
public boolean isJsonNode() {
return true;
}
public JsonNode asJsonNode() {
return this;
}
public ValueNode asValueListNode(Predicate.PredicateContext ctx){
if(!isArray(ctx)){
return UNDEFINED;
} else {
return new ValueListNode(Collections.unmodifiableList((List) parse(ctx)));
}
}
public Object parse(Predicate.PredicateContext ctx){
try {
return parsed ? json : new JSONParser(JSONParser.MODE_PERMISSIVE).parse(json.toString());
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
public boolean isParsed() {
return parsed;
}
public Object getJson() {
return json;
}
public boolean isArray(Predicate.PredicateContext ctx) {
return parse(ctx) instanceof List;
}
public boolean isMap(Predicate.PredicateContext ctx) {
return parse(ctx) instanceof Map;
}
public int length(Predicate.PredicateContext ctx) {
return isArray(ctx) ? ((List<?>) parse(ctx)).size() : -1;
}
public boolean isEmpty(Predicate.PredicateContext ctx) {
if (isArray(ctx) || isMap(ctx)) return ((Collection<?>) parse(ctx)).size() == 0;
else if((parse(ctx) instanceof String)) return ((String)parse(ctx)).length() == 0;
return true;
}
@Override
public String toString() {
return json.toString();
}
public boolean equals(JsonNode jsonNode, Predicate.PredicateContext ctx) {
if (this == jsonNode) return true;
return !(json != null ? !json.equals(jsonNode.parse(ctx)) : jsonNode.json != null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof JsonNode)) return false;
JsonNode jsonNode = (JsonNode) o;
return !(json != null ? !json.equals(jsonNode.json) : jsonNode.json != null);
}
}
public static class StringNode extends ValueNode {
private final String string;
private boolean useSingleQuote = true;
private StringNode(CharSequence charSequence, boolean escape) {
if(charSequence.length() > 1){
char open = charSequence.charAt(0);
char close = charSequence.charAt(charSequence.length()-1);
if(open == '\'' && close == '\''){
charSequence = charSequence.subSequence(1, charSequence.length()-1);
} else if(open == '"' && close == '"'){
charSequence = charSequence.subSequence(1, charSequence.length()-1);
useSingleQuote = false;
}
}
string = escape ? Utils.unescape(charSequence.toString()) : charSequence.toString();
}
@Override
public NumberNode asNumberNode() {
BigDecimal number = null;
try {
number = new BigDecimal(string);
} catch (NumberFormatException nfe){
return NumberNode.NAN;
}
return new NumberNode(number);
}
public String getString() {
return string;
}
public int length(){
return getString().length();
}
public boolean isEmpty(){
return getString().isEmpty();
}
public boolean contains(String str) {
return getString().contains(str);
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return String.class;
}
public boolean isStringNode() {
return true;
}
public StringNode asStringNode() {
return this;
}
@Override
public String toString() {
String quote = useSingleQuote ? "'" : "\"";
return quote + Utils.escape(string, true) + quote;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof StringNode) && !(o instanceof NumberNode)) return false;
StringNode that = ((ValueNode) o).asStringNode();
return !(string != null ? !string.equals(that.getString()) : that.getString() != null);
}
}
public static class NumberNode extends ValueNode {
public static NumberNode NAN = new NumberNode((BigDecimal)null);
private final BigDecimal number;
private NumberNode(BigDecimal number) {
this.number = number;
}
private NumberNode(CharSequence num) {
number = new BigDecimal(num.toString());
}
@Override
public StringNode asStringNode() {
return new StringNode(number.toString(), false);
}
public BigDecimal getNumber() {
return number;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Number.class;
}
public boolean isNumberNode() {
return true;
}
public NumberNode asNumberNode() {
return this;
}
@Override
public String toString() {
return number.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NumberNode) && !(o instanceof StringNode)) return false;
NumberNode that = ((ValueNode)o).asNumberNode();
if(that == NumberNode.NAN){
return false;
} else {
return number.compareTo(that.number) == 0;
}
}
}
public static class BooleanNode extends ValueNode {
private final Boolean value;
private BooleanNode(CharSequence boolValue) {
value = Boolean.parseBoolean(boolValue.toString());
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Boolean.class;
}
public boolean isBooleanNode() {
return true;
}
public BooleanNode asBooleanNode() {
return this;
}
public boolean getBoolean() {
return value;
}
@Override
public String toString() {
return value.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BooleanNode)) return false;
BooleanNode that = (BooleanNode) o;
return !(value != null ? !value.equals(that.value) : that.value != null);
}
}
public static class ClassNode extends ValueNode {
private final Class clazz;
private ClassNode(Class clazz) {
this.clazz = clazz;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Class.class;
}
public boolean isClassNode() {
return true;
}
public ClassNode asClassNode() {
return this;
}
public Class getClazz() {
return clazz;
}
@Override
public String toString() {
return clazz.getName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassNode)) return false;
ClassNode that = (ClassNode) o;
return !(clazz != null ? !clazz.equals(that.clazz) : that.clazz != null);
}
}
public static class NullNode extends ValueNode {
private NullNode() {}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
@Override
public boolean isNullNode() {
return true;
}
@Override
public NullNode asNullNode() {
return this;
}
@Override
public String toString() {
return "null";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NullNode)) return false;
return true;
}
}
public static class UndefinedNode extends ValueNode {
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public UndefinedNode asUndefinedNode() {
return this;
}
public boolean isUndefinedNode() {
return true;
}
@Override
public boolean equals(Object o) {
return false;
}
}
public static class PredicateNode extends ValueNode {
private final Predicate predicate;
public PredicateNode(Predicate predicate) {
this.predicate = predicate;
}
public Predicate getPredicate() {
return predicate;
}
public PredicateNode asPredicateNode() {
return this;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public boolean isPredicateNode() {
return true;
}
@Override
public boolean equals(Object o) {
return false;
}
@Override
public String toString() {
return predicate.toString();
}
}
public static class ValueListNode extends ValueNode implements Iterable<ValueNode> {
private List<ValueNode> nodes = new ArrayList<ValueNode>();
public ValueListNode(Collection<?> values) {
for (Object value : values) {
nodes.add(toValueNode(value));
}
}
public boolean contains(ValueNode node){
return nodes.contains(node);
}
public boolean subsetof(ValueListNode right) {
for (ValueNode leftNode : nodes) {
if (!right.nodes.contains(leftNode)) {
return false;
}
}
return true;
}
public List<ValueNode> getNodes() {
return Collections.unmodifiableList(nodes);
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return List.class;
}
public boolean isValueListNode() {
return true;
}
public ValueListNode asValueListNode() {
return this;
}
@Override
public String toString() {
return "[" + Utils.join(",", nodes) + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ValueListNode)) return false;
ValueListNode that = (ValueListNode) o;
return !(that != null ? !nodes.equals(that.nodes) : that.nodes != null);
}
@Override
public Iterator<ValueNode> iterator() {
return nodes.iterator();
}
}
public static class PathNode extends ValueNode {
private static final Logger logger = LoggerFactory.getLogger(PathNode.class);
private final Path path;
private final boolean existsCheck;
private final boolean shouldExist;
PathNode(Path path) {
this(path, false, false);
}
PathNode(CharSequence charSequence, boolean existsCheck, boolean shouldExist) {
this(PathCompiler.compile(charSequence.toString()), existsCheck, shouldExist);
}
PathNode(Path path, boolean existsCheck, boolean shouldExist) {
this.path = path;
this.existsCheck = existsCheck;
this.shouldExist = shouldExist;
logger.trace("PathNode {} existsCheck: {}", path, existsCheck);
}
public Path getPath() {
return path;
}
public boolean isExistsCheck() {
return existsCheck;
}
public boolean shouldExists() {
return shouldExist;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public boolean isPathNode() {
return true;
}
public PathNode asPathNode() {
return this;
}
public PathNode asExistsCheck(boolean shouldExist) {
return new PathNode(path, true, shouldExist);
}
@Override
public String toString() {
return existsCheck && ! shouldExist ? Utils.concat("!" , path.toString()) : path.toString();
}
public ValueNode evaluate(Predicate.PredicateContext ctx) {
if (isExistsCheck()) {
try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false);
return result == JsonProvider.UNDEFINED ? ValueNode.FALSE : ValueNode.TRUE;
} catch (PathNotFoundException e) {
return ValueNode.FALSE;
}
} else {
try {
Object res;
if (ctx instanceof PredicateContextImpl) {
//This will use cache for document ($) queries
PredicateContextImpl ctxi = (PredicateContextImpl) ctx;
res = ctxi.evaluate(path);
} else {
Object doc = path.isRootPath() ? ctx.root() : ctx.item();
res = path.evaluate(doc, ctx.root(), ctx.configuration()).getValue();
}
res = ctx.configuration().jsonProvider().unwrap(res);
if (res instanceof Number) return ValueNode.createNumberNode(res.toString());
else if (res instanceof BigDecimal) return ValueNode.createNumberNode(res.toString());
else if (res instanceof String) return ValueNode.createStringNode(res.toString(), false);
else if (res instanceof Boolean) return ValueNode.createBooleanNode(res.toString());
else if (res == null) return ValueNode.NULL_NODE;
else if (ctx.configuration().jsonProvider().isArray(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, List.class, ctx.configuration()));
else if (ctx.configuration().jsonProvider().isMap(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, Map.class, ctx.configuration()));
else throw new JsonPathException("Could not convert " + res.toString() + " to a ValueNode");
} catch (PathNotFoundException e) {
return ValueNode.UNDEFINED;
}
}
}
}
}

661
json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java

@ -0,0 +1,661 @@
package com.jayway.jsonpath.internal.filter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.path.PathCompiler;
import com.jayway.jsonpath.internal.path.PredicateContextImpl;
import com.jayway.jsonpath.spi.json.JsonProvider;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Moved these nodes out of the ValueNode abstract class.
* This is to avoid this possible issue:
*
* Classes that refer to their own subclasses in their static initializers or in static fields.
* Such references can cause JVM-level deadlocks in multithreaded environment, when
* one thread tries to load superclass and another thread tries to load subclass at the same time.
*/
public interface ValueNodes {
NullNode NULL_NODE = new NullNode();
BooleanNode TRUE = new BooleanNode("true");
BooleanNode FALSE = new BooleanNode("false");
UndefinedNode UNDEFINED = new UndefinedNode();
//----------------------------------------------------
//
// ValueNode Implementations
//
//----------------------------------------------------
class PatternNode extends ValueNode {
private final String pattern;
private final Pattern compiledPattern;
private final String flags;
PatternNode(CharSequence charSequence) {
String tmp = charSequence.toString();
int begin = tmp.indexOf('/');
int end = tmp.lastIndexOf('/');
this.pattern = tmp.substring(begin + 1, end);
int flagsIndex = end + 1;
this.flags = tmp.length() > flagsIndex ? tmp.substring(flagsIndex) : "";
this.compiledPattern = Pattern.compile(pattern, PatternFlag.parseFlags(flags.toCharArray()));
}
PatternNode(Pattern pattern) {
this.pattern = pattern.pattern();
this.compiledPattern = pattern;
this.flags = PatternFlag.parseFlags(pattern.flags());
}
Pattern getCompiledPattern() {
return compiledPattern;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.TYPE;
}
public boolean isPatternNode() {
return true;
}
public PatternNode asPatternNode() {
return this;
}
@Override
public String toString() {
if(!pattern.startsWith("/")){
return "/" + pattern + "/" + flags;
} else {
return pattern;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PatternNode)) return false;
PatternNode that = (PatternNode) o;
return !(compiledPattern != null ? !compiledPattern.equals(that.compiledPattern) : that.compiledPattern != null);
}
}
class JsonNode extends ValueNode {
private final Object json;
private final boolean parsed;
JsonNode(CharSequence charSequence) {
json = charSequence.toString();
parsed = false;
}
JsonNode(Object parsedJson) {
json = parsedJson;
parsed = true;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
if(isArray(ctx)) return List.class;
else if(isMap(ctx)) return Map.class;
else if(parse(ctx) instanceof Number) return Number.class;
else if(parse(ctx) instanceof String) return String.class;
else if(parse(ctx) instanceof Boolean) return Boolean.class;
else return Void.class;
}
public boolean isJsonNode() {
return true;
}
public JsonNode asJsonNode() {
return this;
}
public ValueNode asValueListNode(Predicate.PredicateContext ctx){
if(!isArray(ctx)){
return UNDEFINED;
} else {
return new ValueListNode(Collections.unmodifiableList((List) parse(ctx)));
}
}
public Object parse(Predicate.PredicateContext ctx){
try {
return parsed ? json : new JSONParser(JSONParser.MODE_PERMISSIVE).parse(json.toString());
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
public boolean isParsed() {
return parsed;
}
public Object getJson() {
return json;
}
public boolean isArray(Predicate.PredicateContext ctx) {
return parse(ctx) instanceof List;
}
public boolean isMap(Predicate.PredicateContext ctx) {
return parse(ctx) instanceof Map;
}
public int length(Predicate.PredicateContext ctx) {
return isArray(ctx) ? ((List<?>) parse(ctx)).size() : -1;
}
public boolean isEmpty(Predicate.PredicateContext ctx) {
if (isArray(ctx) || isMap(ctx)) return ((Collection<?>) parse(ctx)).size() == 0;
else if((parse(ctx) instanceof String)) return ((String)parse(ctx)).length() == 0;
return true;
}
@Override
public String toString() {
return json.toString();
}
public boolean equals(JsonNode jsonNode, Predicate.PredicateContext ctx) {
if (this == jsonNode) return true;
return !(json != null ? !json.equals(jsonNode.parse(ctx)) : jsonNode.json != null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof JsonNode)) return false;
JsonNode jsonNode = (JsonNode) o;
return !(json != null ? !json.equals(jsonNode.json) : jsonNode.json != null);
}
}
class StringNode extends ValueNode {
private final String string;
private boolean useSingleQuote = true;
StringNode(CharSequence charSequence, boolean escape) {
if (escape && charSequence.length() > 1) {
char open = charSequence.charAt(0);
char close = charSequence.charAt(charSequence.length()-1);
if (open == '\'' && close == '\'') {
charSequence = charSequence.subSequence(1, charSequence.length()-1);
} else if (open == '"' && close == '"') {
charSequence = charSequence.subSequence(1, charSequence.length()-1);
useSingleQuote = false;
}
string = Utils.unescape(charSequence.toString());
} else {
string = charSequence.toString();
}
}
@Override
public NumberNode asNumberNode() {
BigDecimal number = null;
try {
number = new BigDecimal(string);
} catch (NumberFormatException nfe){
return NumberNode.NAN;
}
return new NumberNode(number);
}
public String getString() {
return string;
}
public int length(){
return getString().length();
}
public boolean isEmpty(){
return getString().isEmpty();
}
public boolean contains(String str) {
return getString().contains(str);
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return String.class;
}
public boolean isStringNode() {
return true;
}
public StringNode asStringNode() {
return this;
}
@Override
public String toString() {
String quote = useSingleQuote ? "'" : "\"";
return quote + Utils.escape(string, true) + quote;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof StringNode) && !(o instanceof NumberNode)) return false;
StringNode that = ((ValueNode) o).asStringNode();
return !(string != null ? !string.equals(that.getString()) : that.getString() != null);
}
}
class NumberNode extends ValueNode {
public static NumberNode NAN = new NumberNode((BigDecimal)null);
private final BigDecimal number;
NumberNode(BigDecimal number) {
this.number = number;
}
NumberNode(CharSequence num) {
number = new BigDecimal(num.toString());
}
@Override
public StringNode asStringNode() {
return new StringNode(number.toString(), false);
}
public BigDecimal getNumber() {
return number;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Number.class;
}
public boolean isNumberNode() {
return true;
}
public NumberNode asNumberNode() {
return this;
}
@Override
public String toString() {
return number.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NumberNode) && !(o instanceof StringNode)) return false;
NumberNode that = ((ValueNode)o).asNumberNode();
if(that == NumberNode.NAN){
return false;
} else {
return number.compareTo(that.number) == 0;
}
}
}
class BooleanNode extends ValueNode {
private final Boolean value;
private BooleanNode(CharSequence boolValue) {
value = Boolean.parseBoolean(boolValue.toString());
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Boolean.class;
}
public boolean isBooleanNode() {
return true;
}
public BooleanNode asBooleanNode() {
return this;
}
public boolean getBoolean() {
return value;
}
@Override
public String toString() {
return value.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BooleanNode)) return false;
BooleanNode that = (BooleanNode) o;
return !(value != null ? !value.equals(that.value) : that.value != null);
}
}
class ClassNode extends ValueNode {
private final Class clazz;
ClassNode(Class clazz) {
this.clazz = clazz;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Class.class;
}
public boolean isClassNode() {
return true;
}
public ClassNode asClassNode() {
return this;
}
public Class getClazz() {
return clazz;
}
@Override
public String toString() {
return clazz.getName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassNode)) return false;
ClassNode that = (ClassNode) o;
return !(clazz != null ? !clazz.equals(that.clazz) : that.clazz != null);
}
}
class NullNode extends ValueNode {
private NullNode() {}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
@Override
public boolean isNullNode() {
return true;
}
@Override
public NullNode asNullNode() {
return this;
}
@Override
public String toString() {
return "null";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NullNode)) return false;
return true;
}
}
class UndefinedNode extends ValueNode {
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public UndefinedNode asUndefinedNode() {
return this;
}
public boolean isUndefinedNode() {
return true;
}
@Override
public boolean equals(Object o) {
return false;
}
}
class PredicateNode extends ValueNode {
private final Predicate predicate;
public PredicateNode(Predicate predicate) {
this.predicate = predicate;
}
public Predicate getPredicate() {
return predicate;
}
public PredicateNode asPredicateNode() {
return this;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public boolean isPredicateNode() {
return true;
}
@Override
public boolean equals(Object o) {
return false;
}
@Override
public String toString() {
return predicate.toString();
}
}
class ValueListNode extends ValueNode implements Iterable<ValueNode> {
private List<ValueNode> nodes = new ArrayList<ValueNode>();
public ValueListNode(Collection<?> values) {
for (Object value : values) {
nodes.add(toValueNode(value));
}
}
public boolean contains(ValueNode node){
return nodes.contains(node);
}
public boolean subsetof(ValueListNode right) {
for (ValueNode leftNode : nodes) {
if (!right.nodes.contains(leftNode)) {
return false;
}
}
return true;
}
public List<ValueNode> getNodes() {
return Collections.unmodifiableList(nodes);
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return List.class;
}
public boolean isValueListNode() {
return true;
}
public ValueListNode asValueListNode() {
return this;
}
@Override
public String toString() {
return "[" + Utils.join(",", nodes) + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ValueListNode)) return false;
ValueListNode that = (ValueListNode) o;
return nodes.equals(that.nodes);
}
@Override
public Iterator<ValueNode> iterator() {
return nodes.iterator();
}
}
class PathNode extends ValueNode {
private static final Logger logger = LoggerFactory.getLogger(PathNode.class);
private final Path path;
private final boolean existsCheck;
private final boolean shouldExist;
PathNode(Path path) {
this(path, false, false);
}
PathNode(CharSequence charSequence, boolean existsCheck, boolean shouldExist) {
this(PathCompiler.compile(charSequence.toString()), existsCheck, shouldExist);
}
PathNode(Path path, boolean existsCheck, boolean shouldExist) {
this.path = path;
this.existsCheck = existsCheck;
this.shouldExist = shouldExist;
logger.trace("PathNode {} existsCheck: {}", path, existsCheck);
}
public Path getPath() {
return path;
}
public boolean isExistsCheck() {
return existsCheck;
}
public boolean shouldExists() {
return shouldExist;
}
@Override
public Class<?> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public boolean isPathNode() {
return true;
}
public PathNode asPathNode() {
return this;
}
public PathNode asExistsCheck(boolean shouldExist) {
return new PathNode(path, true, shouldExist);
}
@Override
public String toString() {
return existsCheck && ! shouldExist ? Utils.concat("!" , path.toString()) : path.toString();
}
public ValueNode evaluate(Predicate.PredicateContext ctx) {
if (isExistsCheck()) {
try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false);
return result == JsonProvider.UNDEFINED ? FALSE : TRUE;
} catch (PathNotFoundException e) {
return FALSE;
}
} else {
try {
Object res;
if (ctx instanceof PredicateContextImpl) {
//This will use cache for document ($) queries
PredicateContextImpl ctxi = (PredicateContextImpl) ctx;
res = ctxi.evaluate(path);
} else {
Object doc = path.isRootPath() ? ctx.root() : ctx.item();
res = path.evaluate(doc, ctx.root(), ctx.configuration()).getValue();
}
res = ctx.configuration().jsonProvider().unwrap(res);
if (res instanceof Number) return ValueNode.createNumberNode(res.toString());
else if (res instanceof String) return ValueNode.createStringNode(res.toString(), false);
else if (res instanceof Boolean) return ValueNode.createBooleanNode(res.toString());
else if (res == null) return NULL_NODE;
else if (ctx.configuration().jsonProvider().isArray(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, List.class, ctx.configuration()));
else if (ctx.configuration().jsonProvider().isMap(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, Map.class, ctx.configuration()));
else throw new JsonPathException("Could not convert " + res.toString() + " to a ValueNode");
} catch (PathNotFoundException e) {
return UNDEFINED;
}
}
}
}
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunction.java

@ -30,7 +30,7 @@ public interface PathFunction {
* Eval context, state bag used as the path is traversed, maintains the result of executing
*
* @param parameters
* @return
* @return result
*/
Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List<Parameter> parameters);
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/JsonLateBindingValue.java

@ -34,7 +34,7 @@ public class JsonLateBindingValue implements ILateBindingValue {
* Evaluate the JSON document at the point of need using the JSON parameter and associated document model which may
* itself originate from yet another function thus recursively invoking late binding methods.
*
* @return
* @return the late value
*/
@Override
public Object get() {

3
json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/PathLateBindingValue.java

@ -16,7 +16,6 @@ package com.jayway.jsonpath.internal.function.latebinding;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.function.ParamType;
/**
* Defines the contract for late bindings, provides document state and enough context to perform the evaluation at a later
@ -39,7 +38,7 @@ public class PathLateBindingValue implements ILateBindingValue {
/**
* Evaluate the expression at the point of need for Path type expressions
*
* @return
* @return the late value
*/
public Object get() {
return path.evaluate(rootDocument, rootDocument, configuration).getValue();

2
json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java

@ -15,7 +15,7 @@ import java.util.List;
public class Concatenate implements PathFunction {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List<Parameter> parameters) {
StringBuffer result = new StringBuffer();
StringBuilder result = new StringBuilder();
if(ctx.configuration().jsonProvider().isArray(model)){
Iterable<?> objects = ctx.configuration().jsonProvider().toIterable(model);
for (Object obj : objects) {

52
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java

@ -0,0 +1,52 @@
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.PathRef;
import static java.lang.String.format;
public class ArrayIndexToken extends ArrayPathToken {
private final ArrayIndexOperation arrayIndexOperation;
ArrayIndexToken(final ArrayIndexOperation arrayIndexOperation) {
this.arrayIndexOperation = arrayIndexOperation;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (!checkArrayModel(currentPath, model, ctx))
return;
if (arrayIndexOperation.isSingleIndexOperation()) {
handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx);
} else {
for (Integer index : arrayIndexOperation.indexes()) {
handleArrayIndex(index, currentPath, model, ctx);
}
}
}
@Override
public String getPathFragment() {
return arrayIndexOperation.toString();
}
@Override
public boolean isTokenDefinite() {
return arrayIndexOperation.isSingleIndexOperation();
}
}

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

@ -22,143 +22,7 @@ import org.slf4j.LoggerFactory;
import static java.lang.String.format;
/**
*
*/
public class ArrayPathToken extends PathToken {
private static final Logger logger = LoggerFactory.getLogger(ArrayPathToken.class);
private final ArraySliceOperation arraySliceOperation;
private final ArrayIndexOperation arrayIndexOperation;
ArrayPathToken(final ArraySliceOperation arraySliceOperation) {
this.arraySliceOperation = arraySliceOperation;
this.arrayIndexOperation = null;
}
ArrayPathToken(final ArrayIndexOperation arrayIndexOperation) {
this.arrayIndexOperation = arrayIndexOperation;
this.arraySliceOperation = null;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(currentPath, model, ctx))
return;
if(arraySliceOperation != null){
evaluateSliceOperation(currentPath, parent, model, ctx);
} else {
evaluateIndexOperation(currentPath, parent, model, ctx);
}
}
public void evaluateIndexOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(currentPath, model, ctx))
return;
if(arrayIndexOperation.isSingleIndexOperation()){
handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx);
} else {
for (Integer index : arrayIndexOperation.indexes()) {
handleArrayIndex(index, currentPath, model, ctx);
}
}
}
public void evaluateSliceOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(currentPath, model, ctx))
return;
switch (arraySliceOperation.operation()) {
case SLICE_FROM:
sliceFrom(arraySliceOperation, currentPath, parent, model, ctx);
break;
case SLICE_BETWEEN:
sliceBetween(arraySliceOperation, currentPath, parent, model, ctx);
break;
case SLICE_TO:
sliceTo(arraySliceOperation, currentPath, parent, model, ctx);
break;
}
}
public void sliceFrom(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
if (from < 0) {
//calculate slice start from array length
from = length + from;
}
from = Math.max(0, from);
logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString());
if (length == 0 || from >= length) {
return;
}
for (int i = from; i < length; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
public void sliceBetween(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
int to = operation.to();
to = Math.min(length, to);
if (from >= to || length == 0) {
return;
}
logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString());
for (int i = from; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
public void sliceTo(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
if (length == 0) {
return;
}
int to = operation.to();
if (to < 0) {
//calculate slice end from array length
to = length + to;
}
to = Math.min(length, to);
logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString());
for (int i = 0; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
@Override
public String getPathFragment() {
if(arrayIndexOperation != null){
return arrayIndexOperation.toString();
} else {
return arraySliceOperation.toString();
}
}
@Override
public boolean isTokenDefinite() {
if(arrayIndexOperation != null){
return arrayIndexOperation.isSingleIndexOperation();
} else {
return false;
}
}
public abstract class ArrayPathToken extends PathToken {
/**
* Check if model is non-null and array.

18
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java

@ -58,19 +58,19 @@ public class ArraySliceOperation {
Integer tempFrom = tryRead(tokens, 0);
Integer tempTo = tryRead(tokens, 1);
Operation tempOperpation;
if(tempFrom != null && tempTo == null){
tempOperpation = Operation.SLICE_FROM;
} else if(tempFrom != null && tempTo != null){
tempOperpation = Operation.SLICE_BETWEEN;
} else if(tempFrom == null && tempTo != null){
tempOperpation = Operation.SLICE_TO;
Operation tempOperation;
if (tempFrom != null && tempTo == null) {
tempOperation = Operation.SLICE_FROM;
} else if (tempFrom != null) {
tempOperation = Operation.SLICE_BETWEEN;
} else if (tempTo != null) {
tempOperation = Operation.SLICE_TO;
} else {
throw new InvalidPathException("Failed to parse SliceOperation: " + operation);
}
return new ArraySliceOperation(tempFrom, tempTo, tempOperpation);
return new ArraySliceOperation(tempFrom, tempTo, tempOperation);
}
private static Integer tryRead(String[] tokens, int idx){

114
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java

@ -0,0 +1,114 @@
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.PathRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ArraySliceToken extends ArrayPathToken {
private static final Logger logger = LoggerFactory.getLogger(ArraySliceToken.class);
private final ArraySliceOperation operation;
ArraySliceToken(final ArraySliceOperation operation) {
this.operation = operation;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (!checkArrayModel(currentPath, model, ctx))
return;
switch (operation.operation()) {
case SLICE_FROM:
sliceFrom(currentPath, parent, model, ctx);
break;
case SLICE_BETWEEN:
sliceBetween(currentPath, parent, model, ctx);
break;
case SLICE_TO:
sliceTo(currentPath, parent, model, ctx);
break;
}
}
private void sliceFrom(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
if (from < 0) {
//calculate slice start from array length
from = length + from;
}
from = Math.max(0, from);
logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString());
if (length == 0 || from >= length) {
return;
}
for (int i = from; i < length; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
private void sliceBetween(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
int to = operation.to();
to = Math.min(length, to);
if (from >= to || length == 0) {
return;
}
logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString());
for (int i = from; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
private void sliceTo(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
if (length == 0) {
return;
}
int to = operation.to();
if (to < 0) {
//calculate slice end from array length
to = length + to;
}
to = Math.min(length, to);
logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString());
for (int i = 0; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
@Override
public String getPathFragment() {
return operation.toString();
}
@Override
public boolean isTokenDefinite() {
return false;
}
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java

@ -97,7 +97,7 @@ public class CompiledPath implements Path {
try {
PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP;
root.evaluate("", op, document, ctx);
} catch (EvaluationAbortException abort){};
} catch (EvaluationAbortException abort) {}
return ctx;
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/path/FunctionPathToken.java

@ -68,7 +68,7 @@ public class FunctionPathToken extends PathToken {
* Return the actual value by indicating true. If this return was false then we'd return the value in an array which
* isn't what is desired - true indicates the raw value is returned.
*
* @return
* @return true if token is definite
*/
@Override
public boolean isTokenDefinite() {

15
json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java

@ -117,7 +117,7 @@ public class PathCompiler {
path.incrementPosition(1);
if(path.currentChar() != PERIOD && path.currentChar() != OPEN_SQUARE_BRACKET){
fail("Illegal character at position " + path.position() + " expected '.' or '[");
fail("Illegal character at position " + path.position() + " expected '.' or '['");
}
PathTokenAppender appender = pathToken.getPathTokenAppender();
@ -195,7 +195,7 @@ public class PathCompiler {
}
else if (c == OPEN_PARENTHESIS) {
isFunction = true;
endPosition = readPosition++;
endPosition = readPosition;
break;
}
readPosition++;
@ -275,7 +275,7 @@ public class PathCompiler {
Boolean endOfStream = false;
char priorChar = 0;
List<Parameter> parameters = new ArrayList<Parameter>();
StringBuffer parameter = new StringBuffer();
StringBuilder parameter = new StringBuilder();
while (path.inBounds() && !endOfStream) {
char c = path.currentChar();
path.incrementPosition(1);
@ -484,7 +484,8 @@ public class PathCompiler {
if (inBracket) {
int wildCardIndex = path.indexOfNextSignificantChar(WILDCARD);
if (!path.nextSignificantCharIs(wildCardIndex, CLOSE_SQUARE_BRACKET)) {
throw new InvalidPathException("Expected wildcard token to end with ']' on position " + wildCardIndex + 1);
int offset = wildCardIndex + 1;
throw new InvalidPathException("Expected wildcard token to end with ']' on position " + offset);
}
int bracketCloseIndex = path.indexOfNextSignificantChar(wildCardIndex, CLOSE_SQUARE_BRACKET);
path.setPosition(bracketCloseIndex + 1);
@ -580,7 +581,7 @@ public class PathCompiler {
}
break;
} else if (c == potentialStringDelimiter) {
if (inProperty && !inEscape) {
if (inProperty) {
char nextSignificantChar = path.nextSignificantChar(readPosition);
if (nextSignificantChar != CLOSE_SQUARE_BRACKET && nextSignificantChar != COMMA) {
fail("Property must be separated by comma or Property must be terminated close square bracket at index "+readPosition);
@ -603,6 +604,10 @@ public class PathCompiler {
readPosition++;
}
if (inProperty){
fail("Property has not been closed - missing closing " + potentialStringDelimiter);
}
int endBracketIndex = path.indexOfNextSignificantChar(endPosition, CLOSE_SQUARE_BRACKET) + 1;
path.setPosition(endBracketIndex);

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

@ -23,11 +23,11 @@ public class PathTokenFactory {
}
public static PathToken createSliceArrayPathToken(final ArraySliceOperation arraySliceOperation) {
return new ArrayPathToken(arraySliceOperation);
return new ArraySliceToken(arraySliceOperation);
}
public static PathToken createIndexArrayPathToken(final ArrayIndexOperation arrayIndexOperation) {
return new ArrayPathToken(arrayIndexOperation);
return new ArrayIndexToken(arrayIndexOperation);
}
public static PathToken createWildCardPathToken() {

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

@ -20,6 +20,7 @@ import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.PathRef;
import java.util.Collection;
import java.util.Collections;
import static java.lang.String.format;
import static java.util.Arrays.asList;
@ -33,7 +34,7 @@ public class PredicatePathToken extends PathToken {
private final Collection<Predicate> predicates;
PredicatePathToken(Predicate filter) {
this.predicates = asList(filter);
this.predicates = Collections.singletonList(filter);
}
PredicatePathToken(Collection<Predicate> predicates) {

2
json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java

@ -27,7 +27,7 @@ public class RootPathToken extends PathToken {
RootPathToken(char rootToken) {
this.rootToken = Character.toString(rootToken);;
this.rootToken = Character.toString(rootToken);
this.tail = this;
this.tokenCount = 1;
}

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

@ -14,6 +14,8 @@
*/
package com.jayway.jsonpath.internal.path;
import java.util.Collections;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
@ -32,7 +34,7 @@ public class WildcardPathToken extends PathToken {
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
for (String property : ctx.jsonProvider().getPropertyKeys(model)) {
handleObjectProperty(currentPath, model, ctx, asList(property));
handleObjectProperty(currentPath, model, ctx, Collections.singletonList(property));
}
} else if (ctx.jsonProvider().isArray(model)) {
for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) {

5
json-path/src/main/java/com/jayway/jsonpath/spi/cache/Cache.java vendored

@ -10,14 +10,13 @@ public interface Cache {
* @param key cache key to lookup the JsonPath
* @return JsonPath
*/
public JsonPath get(String key);
JsonPath get(String key);
/**
* Add JsonPath to the cache
* @param key cache key to store the JsonPath
* @param value JsonPath to be cached
* @return void
* @throws InvalidJsonException
*/
public void put(String key, JsonPath value);
void put(String key, JsonPath value);
}

4
json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java vendored

@ -6,7 +6,6 @@ import static com.jayway.jsonpath.internal.Utils.notNull;
public class CacheProvider {
private static Cache cache;
private static boolean cachingEnabled;
public static void setCache(Cache cache){
notNull(cache, "Cache may not be null");
@ -16,7 +15,6 @@ public class CacheProvider {
} else {
CacheProvider.cache = cache;
}
cachingEnabled = !(CacheProvider.cache instanceof NOOPCache);
}
}
@ -36,4 +34,4 @@ public class CacheProvider {
return new LRUCache(400);
//return new NOOPCache();
}
}
}

3
json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java

@ -43,6 +43,7 @@ public abstract class AbstractJsonProvider implements JsonProvider {
return ((List) obj).get(idx);
}
@Deprecated
public final Object getArrayIndex(Object obj, int idx, boolean unwrap){
return getArrayIndex(obj, idx);
}
@ -162,7 +163,7 @@ public abstract class AbstractJsonProvider implements JsonProvider {
* @return an Iterable that iterates over the entries of an array
*/
@SuppressWarnings("unchecked")
public Iterable<? extends Object> toIterable(Object obj) {
public Iterable<?> toIterable(Object obj) {
if (isArray(obj))
return ((Iterable) obj);
else

2
json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java

@ -270,7 +270,7 @@ public class JacksonJsonNodeJsonProvider extends AbstractJsonProvider {
} else if (value == null) {
objectNode.set(key.toString(), null); // this will create a null-node
} else {
objectNode.put(key.toString(), createJsonElement(value));
objectNode.set(key.toString(), createJsonElement(value));
}
}

6
json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java

@ -30,7 +30,7 @@ import java.util.List;
public class JacksonJsonProvider extends AbstractJsonProvider {
private static final ObjectMapper defaultObjectMapper = new ObjectMapper();
private static final ObjectReader defaultObjectReader = defaultObjectMapper.reader().withType(Object.class);
private static final ObjectReader defaultObjectReader = defaultObjectMapper.reader().forType(Object.class);
protected ObjectMapper objectMapper;
protected ObjectReader objectReader;
@ -51,7 +51,7 @@ public class JacksonJsonProvider extends AbstractJsonProvider {
* @param objectMapper the ObjectMapper to use
*/
public JacksonJsonProvider(ObjectMapper objectMapper) {
this(objectMapper, objectMapper.reader().withType(Object.class));
this(objectMapper, objectMapper.reader().forType(Object.class));
}
/**
@ -93,7 +93,7 @@ public class JacksonJsonProvider extends AbstractJsonProvider {
generator.close();
return writer.getBuffer().toString();
} catch (IOException e) {
throw new InvalidJsonException();
throw new InvalidJsonException(e);
}
}

20
json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java

@ -1,9 +1,9 @@
package com.jayway.jsonpath;
import java.util.Collections;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.util.Collections;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria.where;
@ -152,24 +152,6 @@ public class FilterParseTest {
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_anyof_filter_can_be_serialized() {
String filter = filter(where("a").anyof(Collections.emptyList())).toString();
String parsed = parse("[?(@['a'] ANYOF [])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_noneof_filter_can_be_serialized() {
String filter = filter(where("a").noneof(Collections.emptyList())).toString();
String parsed = parse("[?(@['a'] NONEOF [])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_exists_filter_can_be_serialized() {

52
json-path/src/test/java/com/jayway/jsonpath/FilterTest.java

@ -1,5 +1,6 @@
package com.jayway.jsonpath;
import java.util.ArrayList;
import org.assertj.core.util.Lists;
import org.junit.Test;
@ -278,7 +279,8 @@ public class FilterTest extends BaseTest {
Filter farr = parse("[?(@.foo == " + arr + ")]");
//Filter fobjF = parse("[?(@.foo == " + nest + ")]");
//Filter fobjT = parse("[?(@.bar == " + nest + ")]");
assertThat(farr.apply(context)).isEqualTo(true);
boolean apply = farr.apply(context);
assertThat(apply).isEqualTo(true);
//assertThat(fobjF.apply(context)).isEqualTo(false);
//assertThat(fobjT.apply(context)).isEqualTo(true);
}
@ -377,36 +379,6 @@ public class FilterTest extends BaseTest {
assertThat(filter(where("string-arr").subsetof(list)).apply(createPredicateContext(json))).isEqualTo(false);
}
//----------------------------------------------------------------------------
//
// ANYOF
//
//----------------------------------------------------------------------------
@Test
public void array_anyof_evals() {
List<String> list = Lists.newArrayList("a", "z");
assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(true);
list = Lists.newArrayList("z", "b", "a");
assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(true);
list = Lists.newArrayList("x", "y", "z");
assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(false);
}
//----------------------------------------------------------------------------
//
// NONEOF
//
//----------------------------------------------------------------------------
@Test
public void array_noneof_evals() {
List<String> list = Lists.newArrayList("a", "z");
assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(false);
list = Lists.newArrayList("z", "b", "a");
assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(false);
list = Lists.newArrayList("x", "y", "z");
assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(true);
}
//----------------------------------------------------------------------------
//
// EXISTS
@ -446,13 +418,13 @@ public class FilterTest extends BaseTest {
//----------------------------------------------------------------------------
@Test
public void not_empty_evals() {
assertThat(filter(where("string-key").notEmpty()).apply(createPredicateContext(json))).isEqualTo(true);
assertThat(filter(where("string-key-empty").notEmpty()).apply(createPredicateContext(json))).isEqualTo(false);
assertThat(filter(where("string-key").empty(false)).apply(createPredicateContext(json))).isEqualTo(true);
assertThat(filter(where("string-key-empty").empty(false)).apply(createPredicateContext(json))).isEqualTo(false);
assertThat(filter(where("int-arr").notEmpty()).apply(createPredicateContext(json))).isEqualTo(true);
assertThat(filter(where("arr-empty").notEmpty()).apply(createPredicateContext(json))).isEqualTo(false);
assertThat(filter(where("int-arr").empty(false)).apply(createPredicateContext(json))).isEqualTo(true);
assertThat(filter(where("arr-empty").empty(false)).apply(createPredicateContext(json))).isEqualTo(false);
assertThat(filter(where("null-key").notEmpty()).apply(createPredicateContext(json))).isEqualTo(false);
assertThat(filter(where("null-key").empty(false)).apply(createPredicateContext(json))).isEqualTo(false);
}
//----------------------------------------------------------------------------
@ -544,11 +516,11 @@ public class FilterTest extends BaseTest {
@Test
public void criteria_can_be_parsed() {
Criteria criteria = Criteria.parse("@.foo == 'baar'");
assertThat(criteria.toString()).isEqualTo("@['foo'] == 'baar'");
Filter criteria = Filter.parse("[?(@.foo == 'baar')]");
assertThat(criteria.toString()).isEqualTo("[?(@['foo'] == 'baar')]");
criteria = Criteria.parse("@.foo");
assertThat(criteria.toString()).isEqualTo("@['foo']");
criteria = Filter.parse("[?(@.foo)]");
assertThat(criteria.toString()).isEqualTo("[?(@['foo'])]");
}

35
json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java

@ -3,9 +3,13 @@ package com.jayway.jsonpath;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingException;
import org.junit.Test;
@ -55,7 +59,7 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest {
context.put("$", "child", child1);
ObjectNode node2 = context.read("$");
ObjectNode child2 = context.read("$.child");
assertThat(node1).isSameAs(node2);
assertThat(child1).isSameAs(child2);
}
@ -112,7 +116,32 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest {
using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON).read("$", typeRef);
}
@Test
public void mapPropertyWithPOJO() {
String someJson = "" +
"{\n" +
" \"a\": \"a\",\n" +
" \"b\": \"b\"\n" +
"}";
ObjectMapper om = new ObjectMapper();
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
Configuration c = Configuration
.builder()
.mappingProvider(new JacksonMappingProvider())
.jsonProvider(new JacksonJsonNodeJsonProvider(om))
.build();
DocumentContext context = JsonPath.using(c).parse(someJson);
String someJsonStr = context.jsonString();
DocumentContext altered = context.map("$['a', 'b', 'c']", new MapFunction() {
@Override
public Object map(Object currentValue, Configuration configuration) {
return currentValue;
}
});
assertThat(altered.jsonString()).isEqualTo(someJsonStr);
}
@Test
// https://github.com/json-path/JsonPath/issues/364
public void setPropertyWithPOJO() {
@ -161,7 +190,7 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest {
public static class Gen {
public String eric;
}
public static final class Data {
@JsonProperty("id")
UUID id;

3
json-path/src/test/java/com/jayway/jsonpath/MultiPropTest.java

@ -3,6 +3,7 @@ package com.jayway.jsonpath;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.jayway.jsonpath.JsonPath.using;
@ -114,7 +115,7 @@ public class MultiPropTest {
final Configuration conf = Configuration.defaultConfiguration().addOptions(Option.REQUIRE_PROPERTIES);
final String json = "{\"a\": {\"v\": 5}, \"b\": {\"v\": 4}, \"c\": {\"v\": 1}}";
assertThat(using(conf).parse(json).read("$['a', 'c'].v")).asList().containsOnly(5, 1);
assertThat((List)using(conf).parse(json).read("$['a', 'c'].v")).asList().containsOnly(5, 1);
assertEvaluationThrows(json, "$['d', 'a', 'c', 'm'].v", PathNotFoundException.class, conf);
}
}

24
json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java

@ -21,7 +21,7 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.defaultConfiguration();
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.baz")).isNull();
assertThat((String)using(conf).parse("{\"foo\" : \"bar\"}").read("$.baz")).isNull();
}
@Test
@ -37,7 +37,7 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.defaultConfiguration();
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(String.class);
assertThat((String)using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(String.class);
}
@Test
@ -45,11 +45,11 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.builder().options(ALWAYS_RETURN_LIST).build();
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(List.class);
assertThat((List)using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(List.class);
assertThat(using(conf).parse("{\"foo\": null}").read("$.foo")).isInstanceOf(List.class);
assertThat((List)using(conf).parse("{\"foo\": null}").read("$.foo")).isInstanceOf(List.class);
assertThat(using(conf).parse("{\"foo\": [1, 4, 8]}").read("$.foo")).asList()
assertThat((List)using(conf).parse("{\"foo\": [1, 4, 8]}").read("$.foo")).asList()
.containsExactly(Arrays.asList(1, 4, 8));
}
@ -61,7 +61,7 @@ public class OptionsTest extends BaseTest {
assertThat(result).hasSize(1);
assertThat(result.get(0)).isNull();
assertThat(using(conf).parse("{\"bar\": {\"foo\": [1, 4, 8]}}").read("$..foo")).asList()
assertThat((List)using(conf).parse("{\"bar\": {\"foo\": [1, 4, 8]}}").read("$..foo")).asList()
.containsExactly(Arrays.asList(1, 4, 8));
}
@ -69,7 +69,7 @@ public class OptionsTest extends BaseTest {
public void a_path_evaluation_is_returned_as_VALUE_by_default() {
Configuration conf = Configuration.defaultConfiguration();
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isEqualTo("bar");
assertThat((String)using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isEqualTo("bar");
}
@Test
@ -142,13 +142,13 @@ public class OptionsTest extends BaseTest {
public void issue_suppress_exceptions_does_not_break_indefinite_evaluation() {
Configuration conf = Configuration.builder().options(SUPPRESS_EXCEPTIONS).build();
assertThat(using(conf).parse("{\"foo2\": [5]}").read("$..foo2[0]")).asList().containsOnly(5);
assertThat(using(conf).parse("{\"foo\" : {\"foo2\": [5]}}").read("$..foo2[0]")).asList().containsOnly(5);
assertThat(using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo2[0]")).asList().containsOnly(5);
assertThat((List)using(conf).parse("{\"foo2\": [5]}").read("$..foo2[0]")).asList().containsOnly(5);
assertThat((List)using(conf).parse("{\"foo\" : {\"foo2\": [5]}}").read("$..foo2[0]")).asList().containsOnly(5);
assertThat((List)using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo2[0]")).asList().containsOnly(5);
assertThat(using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo.foo2[0]")).asList().containsOnly(5);
assertThat((List)using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo.foo2[0]")).asList().containsOnly(5);
assertThat(using(conf).parse("{\"aoo\" : {}, \"foo\" : {\"foo2\": [5]}, \"zoo\" : {}}").read("$[*].foo2[0]")).asList().containsOnly(5);
assertThat((List)using(conf).parse("{\"aoo\" : {}, \"foo\" : {\"foo2\": [5]}, \"zoo\" : {}}").read("$[*].foo2[0]")).asList().containsOnly(5);
}
@Test

52
json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java

@ -1,5 +1,6 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.ParseContextImpl;
import org.junit.Ignore;
import org.junit.Test;
@ -236,6 +237,49 @@ public class PathCompilerTest {
assertThat(result).hasSize(1);
}
@Test
public void issue_predicate_can_have_double_quotes() {
String json = "{\n"
+ " \"logs\": [\n"
+ " {\n"
+ " \"message\": \"\\\"it\\\"\",\n"
+ " }\n"
+ " ]\n"
+ "}";
List<String> result = JsonPath.read(json, "$.logs[?(@.message == '\"it\"')].message");
assertThat(result).containsExactly("\"it\"");
}
@Test
public void issue_predicate_can_have_single_quotes() {
String json = "{\n"
+ " \"logs\": [\n"
+ " {\n"
+ " \"message\": \"'it'\",\n"
+ " }\n"
+ " ]\n"
+ "}";
DocumentContext parse = JsonPath.parse(json);
JsonPath compile = JsonPath.compile("$.logs[?(@.message == \"'it'\")].message");
List<String> result = parse.read(compile);
assertThat(result).containsExactly("'it'");
}
@Test
public void issue_predicate_can_have_single_quotes_escaped() {
String json = "{\n"
+ " \"logs\": [\n"
+ " {\n"
+ " \"message\": \"'it'\",\n"
+ " }\n"
+ " ]\n"
+ "}";
DocumentContext parse = JsonPath.parse(json);
JsonPath compile = JsonPath.compile("$.logs[?(@.message == '\\'it\\'')].message");
List<String> result = parse.read(compile);
assertThat(result).containsExactly("'it'");
}
@Test
public void issue_predicate_can_have_square_bracket_in_prop() {
String json = "{\n"
@ -252,6 +296,14 @@ public class PathCompilerTest {
assertThat(result).containsExactly("] it");
}
@Test
public void a_function_can_be_compiled() {
assertThat(compile("$.aaa.foo()").toString()).isEqualTo("$['aaa'].foo()");
assertThat(compile("$.aaa.foo(5)").toString()).isEqualTo("$['aaa'].foo(...)");
assertThat(compile("$.aaa.foo($.bar)").toString()).isEqualTo("$['aaa'].foo(...)");
assertThat(compile("$.aaa.foo(5,10,15)").toString()).isEqualTo("$['aaa'].foo(...)");
}
@Test(expected = InvalidPathException.class)
public void array_indexes_must_be_separated_by_commas() {
compile("$[0, 1, 2 4]");

8
json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java

@ -18,7 +18,7 @@ public class ReturnTypeTest extends BaseTest {
@Test
public void assert_strings_can_be_read() {
assertThat(reader.read("$.string-property")).isEqualTo("string-value");
assertThat((String)reader.read("$.string-property")).isEqualTo("string-value");
}
@Test
@ -28,17 +28,17 @@ public class ReturnTypeTest extends BaseTest {
@Test
public void assert_longs_can_be_read() {
assertThat(reader.read("$.long-max-property")).isEqualTo(Long.MAX_VALUE);
assertThat((Long)reader.read("$.long-max-property")).isEqualTo(Long.MAX_VALUE);
}
@Test
public void assert_boolean_values_can_be_read() {
assertThat(reader.read("$.boolean-property")).isEqualTo(true);
assertThat((Boolean)reader.read("$.boolean-property")).isEqualTo(true);
}
@Test
public void assert_null_values_can_be_read() {
assertThat(reader.read("$.null-property")).isNull();
assertThat((String)reader.read("$.null-property")).isNull();
}
@Test

47
json-path/src/test/java/com/jayway/jsonpath/internal/filter/PatternFlagTest.java

@ -0,0 +1,47 @@
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
@RunWith(Parameterized.class)
public class PatternFlagTest extends BaseTest {
private final int flags;
private final String expectedFlags;
public PatternFlagTest(int flags, String expectedFlags) {
this.flags = flags;
this.expectedFlags = expectedFlags;
}
@Test
public void testParseFlags() {
Assert.assertEquals(expectedFlags, PatternFlag.parseFlags(flags));
}
@Parameterized.Parameters
public static Iterable data() {
return Arrays.asList(
new Object[][]{
{ 1, "d" },
{ 2, "i" },
{ 4, "x" },
{ 8, "m" },
{ 32, "s" },
{ 64, "u" },
{ 256, "U" },
{ 300, "xmsU" },
{ 13, "dxm" },
{ 7, "dix" },
{ 100, "xsu" },
{ 367, "dixmsuU" }
}
);
}
}

33
json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java

@ -11,6 +11,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.HashMap;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
@ -49,14 +50,28 @@ public class RegexpEvaluatorTest extends BaseTest {
public static Iterable data() {
return Arrays.asList(
new Object[][]{
{ "/true|false/", createStringNode("true", true), true },
{ "/9.*9/", createNumberNode("9979"), true },
{ "/fa.*se/", createBooleanNode("false"), true },
{ "/Eval.*or/", createClassNode(String.class), false },
{ "/JsonNode/", createJsonNode(json()), false },
{ "/PathNode/", createPathNode(path()), false },
{ "/Undefined/", createUndefinedNode(), false },
{ "/NullNode/", createNullNode(), false }
{ "/true|false/", createStringNode("true", true), true },
{ "/9.*9/", createNumberNode("9979"), true },
{ "/fa.*se/", createBooleanNode("false"), true },
{ "/Eval.*or/", createClassNode(String.class), false },
{ "/JsonNode/", createJsonNode(json()), false },
{ "/PathNode/", createPathNode(path()), false },
{ "/Undefined/", createUndefinedNode(), false },
{ "/NullNode/", createNullNode(), false },
{ "/test/i", createStringNode("tEsT", true), true },
{ "/test/", createStringNode("tEsT", true), false },
{ "/\u00de/ui", createStringNode("\u00fe", true), true },
{ "/\u00de/", createStringNode("\u00fe", true), false },
{ "/\u00de/i", createStringNode("\u00fe", true), false },
{ "/test# code/", createStringNode("test", true), false },
{ "/test# code/x", createStringNode("test", true), true },
{ "/.*test.*/d", createStringNode("my\rtest", true), true },
{ "/.*test.*/", createStringNode("my\rtest", true), false },
{ "/.*tEst.*/is", createStringNode("test\ntest", true), true },
{ "/.*tEst.*/i", createStringNode("test\ntest", true), false },
{ "/^\\w+$/U", createStringNode("\u00fe", true), true },
{ "/^\\w+$/", createStringNode("\u00fe", true), false },
{ "/^test$\\ntest$/m", createStringNode("test\ntest", true), true }
}
);
}
@ -70,7 +85,7 @@ public class RegexpEvaluatorTest extends BaseTest {
}
private Predicate.PredicateContext createPredicateContext() {
return createPredicateContext(Maps.newHashMap());
return createPredicateContext(new HashMap<>());
}
}

4
json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue234.java

@ -1,9 +1,9 @@
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.JsonPath;
import org.assertj.core.util.Maps;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@ -27,7 +27,7 @@ public class Issue234 {
@Test
public void testIssue234() {
Map<String, String> context = Maps.newHashMap();
Map<String, String> context = new HashMap<>();
context.put("key", "first");
Object value = JsonPath.read(context, "concat(\"/\", $.key)");
assertThat(value).isEqualTo("/first");

6
json-path/src/test/java/com/jayway/jsonpath/internal/function/NestedFunctionTest.java

@ -58,6 +58,12 @@ public class NestedFunctionTest extends BaseFunctionTest {
verifyMathFunction(conf, "$.sum(5, 3, $.numbers.max(), 2)", 20.0);
}
@Test
public void testSimpleLiteralArgument() {
verifyMathFunction(conf, "$.sum(5)", 5.0);
verifyMathFunction(conf, "$.sum(50)", 50.0);
}
@Test
public void testStringConcat() {
verifyTextFunction(conf, "$.text.concat()", "abcdef");

2
json-path/src/test/java/com/jayway/jsonpath/old/ArraySlicingTest.java

@ -6,7 +6,7 @@ import org.junit.Test;
import java.util.List;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**

6
json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java

@ -18,7 +18,7 @@ import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
import static java.util.Arrays.asList;
import static junit.framework.Assert.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@ -409,10 +409,10 @@ public class FilterTest extends BaseTest {
Object doc = Configuration.defaultConfiguration().jsonProvider().parse(json);
List<Map<String, Object>> result = JsonPath.read(doc, "$.fields[?]", filter(where("errors").notEmpty()));
List<Map<String, Object>> result = JsonPath.read(doc, "$.fields[?]", filter(where("errors").empty(false)));
assertEquals(1, result.size());
List<Map<String, Object>> result2 = JsonPath.read(doc, "$.fields[?]", filter(where("name").notEmpty()));
List<Map<String, Object>> result2 = JsonPath.read(doc, "$.fields[?]", filter(where("name").empty(false)));
assertEquals(2, result2.size());
}

18
json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java

@ -35,8 +35,8 @@ import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
import static com.jayway.jsonpath.JsonPath.read;
import static com.jayway.jsonpath.JsonPath.using;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.hamcrest.Matchers.is;
@ -431,7 +431,7 @@ public class IssuesTest extends BaseTest {
"]";
assertEquals(1, read(json, "$[0].a"));
assertEquals(Integer.valueOf(1), read(json, "$[0].a"));
}
@Test(expected = PathNotFoundException.class)
@ -471,9 +471,9 @@ public class IssuesTest extends BaseTest {
String json = "{\"test\":null}";
assertThat(read(json, "test")).isNull();
assertThat((String)read(json, "test")).isNull();
assertThat(JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS)).parse(json).read("nonExistingProperty")).isNull();
assertThat((String)JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS)).parse(json).read("nonExistingProperty")).isNull();
try {
read(json, "nonExistingProperty");
@ -498,7 +498,7 @@ public class IssuesTest extends BaseTest {
public void issue_45() {
String json = "{\"rootkey\":{\"sub.key\":\"value\"}}";
assertThat(read(json, "rootkey['sub.key']")).isEqualTo("value");
assertThat((String)read(json, "rootkey['sub.key']")).isEqualTo("value");
}
@Test
@ -508,7 +508,7 @@ public class IssuesTest extends BaseTest {
String json = "{\"a\": {}}";
Configuration configuration = Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS);
assertThat(JsonPath.using(configuration).parse(json).read("a.x")).isNull();
assertThat((String)JsonPath.using(configuration).parse(json).read("a.x")).isNull();
try {
read(json, "a.x");
@ -1018,8 +1018,8 @@ public class IssuesTest extends BaseTest {
DocumentContext doc = JsonPath.parse(json).set("$.jsonArr[1].name", "Jayway");
assertThat(doc.read("$.jsonArr[0].name")).isEqualTo("nOne");
assertThat(doc.read("$.jsonArr[1].name")).isEqualTo("Jayway");
assertThat((String)doc.read("$.jsonArr[0].name")).isEqualTo("nOne");
assertThat((String)doc.read("$.jsonArr[1].name")).isEqualTo("Jayway");
}
@Test

7
json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java

@ -133,7 +133,7 @@ public class JsonPathTest extends BaseTest {
Assertions.fail("Expected PathNotFoundException");
} catch (PathNotFoundException e) {
}
Assertions.assertThat(JsonPath.read(json, "$.data2.passes[0].id")).isEqualTo("1");
Assertions.assertThat((String)JsonPath.read(json, "$.data2.passes[0].id")).isEqualTo("1");
}
@Test
@ -322,5 +322,10 @@ public class JsonPathTest extends BaseTest {
}
}
@Test(expected = InvalidPathException.class)
//see https://github.com/json-path/JsonPath/issues/428
public void prevent_stack_overflow_error_when_unclosed_property() {
JsonPath.compile("$['boo','foo][?(@ =~ /bar/)]");
}
}

4
json-path/src/test/java/com/jayway/jsonpath/old/NullHandlingTest.java

@ -9,8 +9,8 @@ import org.junit.Test;
import java.util.List;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class NullHandlingTest {

2
json-path/src/test/java/com/jayway/jsonpath/old/internal/PropertyPathTokenTest.java

@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory;
import java.util.List;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertNull;
import static org.assertj.core.api.Assertions.assertThat;
public class PropertyPathTokenTest {

2
system.properties

@ -1 +1 @@
java.runtime.version=1.7
java.runtime.version=1.8
Loading…
Cancel
Save