Browse Source

sync with upstream

pull/142/head
Alexey Makeyev 9 years ago
parent
commit
b10cedc358
  1. 2
      .travis.yml
  2. 23
      README.md
  3. 9
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  4. 33
      json-path/src/main/java/com/jayway/jsonpath/Function.java
  5. 12
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  6. 11
      json-path/src/main/java/com/jayway/jsonpath/internal/CompiledPath.java
  7. 6
      json-path/src/main/java/com/jayway/jsonpath/internal/Path.java
  8. 44
      json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java
  9. 71
      json-path/src/main/java/com/jayway/jsonpath/internal/function/FunctionFactory.java
  10. 26
      json-path/src/main/java/com/jayway/jsonpath/internal/function/Length.java
  11. 18
      json-path/src/main/java/com/jayway/jsonpath/internal/function/PassthruFunction.java
  12. 55
      json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/AbstractAggregation.java
  13. 26
      json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Average.java
  14. 22
      json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Max.java
  15. 22
      json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java
  16. 24
      json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/StandardDeviation.java
  17. 20
      json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Sum.java
  18. 56
      json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java
  19. 14
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java
  20. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PathTokenFactory.java
  21. 7
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PropertyPathToken.java
  22. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/token/RootPathToken.java
  23. 8
      json-path/src/test/java/com/jayway/jsonpath/BaseTest.java
  24. 56
      json-path/src/test/java/com/jayway/jsonpath/Configurations.java
  25. 140
      json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
  26. 11
      json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java
  27. 76
      json-path/src/test/java/com/jayway/jsonpath/JsonProviderTest.java
  28. 75
      json-path/src/test/java/com/jayway/jsonpath/JsonProviderTestObjectMapping.java
  29. 45
      json-path/src/test/java/com/jayway/jsonpath/functions/BaseFunctionTest.java
  30. 105
      json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java
  31. 90
      json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java
  32. 6
      json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java
  33. 174
      json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java

2
.travis.yml

@ -1 +1,3 @@
language: java
sudo: false

23
README.md

@ -61,6 +61,22 @@ Operators
| `[start:end]` | Array slice operator |
| `[?(<expression>)]` | Filter expression. Expression must evaluate to a boolean value. |
<!---
Functions (not released yet)
---------
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 |
-->
Path Examples
-------------
@ -123,9 +139,10 @@ Given the json
| <a href="http://jsonpath.herokuapp.com/?path=$.store.book[?(@.price < 10)]" target="_blank">$.store.book[?(@.price < 10)]</a> | All books in store cheaper than 10 |
| <a href="http://jsonpath.herokuapp.com/?path=$..book[?(@.price <= $['expensive'])]" target="_blank">$..book[?(@.price <= $['expensive'])]</a> | All books in store that are not "expensive" |
| <a href="http://jsonpath.herokuapp.com/?path=$..book[?(@.author =~ /.*REES/i)]" target="_blank">$..book[?(@.author =~ /.*REES/i)]</a> | All books matching regex (ignore case) |
| <a href="http://jsonpath.herokuapp.com/?path=$..*" target="_blank">$..*</a> | Give me every thing |
| <a href="http://jsonpath.herokuapp.com/?path=$..*" target="_blank">$..*</a> | Give me every thing
<!---
| <a href="http://jsonpath.herokuapp.com/?path=$..book.%length()" target="_blank">$..book.%length()</a> | The number of books |
-->
Reading a Document
------------------
The simplest most straight forward way to use JsonPath is via the static read API.

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

@ -445,13 +445,12 @@ public class Criteria implements Predicate {
boolean exists = ((Boolean) right);
try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
Object value = ((Path) left).evaluate(ctx.item(), ctx.root(), c).getValue();
Object value = ((Path) left).evaluate(ctx.item(), ctx.root(), c).getValue(false);
if (exists) {
return (value != null);
return (value != JsonProvider.UNDEFINED);
} else {
return (value == null);
return (value == JsonProvider.UNDEFINED);
}
} catch (PathNotFoundException e) {
return !exists;
}
@ -841,7 +840,7 @@ public class Criteria implements Predicate {
@Override
public String toString() {
return getClass().getSimpleName() + " " + value;
return value;
}
}

33
json-path/src/main/java/com/jayway/jsonpath/Function.java

@ -0,0 +1,33 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
/**
* Defines the pattern by which a function can be executed over the result set in the particular path
* being grabbed. The Function's input is the content of the data from the json path selector and its output
* is defined via the functions behavior. Thus transformations in types can take place. Additionally, functions
* can accept multiple selectors in order to produce their output.
*
* Created by matt@mjgreenwood.net on 6/26/15.
*/
public interface Function {
/**
* Invoke the function and output a JSON object (or scalar) value which will be the result of executing the path
*
* @param currentPath
* The current path location inclusive of the function name
* @param parent
* The path location above the current function
*
* @param model
* The JSON model as input to this particular function
*
* @param ctx
* Eval context, state bag used as the path is traversed, maintains the result of executing
*
* @return
*/
Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx);
}

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

@ -29,6 +29,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import static com.jayway.jsonpath.Option.ALWAYS_RETURN_LIST;
import static com.jayway.jsonpath.Option.AS_PATH_LIST;
import static com.jayway.jsonpath.internal.Utils.*;
/**
@ -167,11 +169,17 @@ public class JsonPath {
*/
@SuppressWarnings("unchecked")
public <T> T read(Object jsonObject, Configuration configuration) {
boolean optAsPathList = configuration.containsOption(Option.AS_PATH_LIST);
boolean optAsPathList = configuration.containsOption(AS_PATH_LIST);
boolean optAlwaysReturnList = configuration.containsOption(Option.ALWAYS_RETURN_LIST);
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
try {
if(path.isFunctionPath()){
if(optAsPathList || optAlwaysReturnList){
throw new JsonPathException("Options " + AS_PATH_LIST + " and " + ALWAYS_RETURN_LIST + " are not allowed when using path functions!");
}
return path.evaluate(jsonObject, jsonObject, configuration).getValue(true);
}
if(optAsPathList){
return (T)path.evaluate(jsonObject, jsonObject, configuration).getPath();
} else {
@ -644,7 +652,7 @@ public class JsonPath {
}
private <T> T resultByConfiguration(Object jsonObject, Configuration configuration, EvaluationContext evaluationContext) {
if(configuration.containsOption(Option.AS_PATH_LIST)){
if(configuration.containsOption(AS_PATH_LIST)){
return (T)evaluationContext.getPathList();
} else {
return (T) jsonObject;

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

@ -16,7 +16,7 @@ package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.internal.token.EvaluationContextImpl;
import com.jayway.jsonpath.internal.token.PathToken;
import com.jayway.jsonpath.internal.token.RootPathToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,12 +24,12 @@ public class CompiledPath implements Path {
private static final Logger logger = LoggerFactory.getLogger(CompiledPath.class);
private final PathToken root;
private final RootPathToken root;
private final boolean isRootPath;
public CompiledPath(PathToken root, boolean isRootPath) {
public CompiledPath(RootPathToken root, boolean isRootPath) {
this.root = root;
this.isRootPath = isRootPath;
}
@ -64,6 +64,11 @@ public class CompiledPath implements Path {
return root.isPathDefinite();
}
@Override
public boolean isFunctionPath() {
return root.isFunctionPath();
}
@Override
public String toString() {
return root.toString();

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

@ -49,6 +49,12 @@ public interface Path {
*/
boolean isDefinite();
/**
*
* @return true id this path is a function
*/
boolean isFunctionPath();
/**
*
* @return true id this path is starts with '$' and false if the path starts with '@'

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

@ -5,11 +5,10 @@ import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.token.ArrayIndexOperation;
import com.jayway.jsonpath.internal.token.ArraySliceOperation;
import com.jayway.jsonpath.internal.token.FunctionPathToken;
import com.jayway.jsonpath.internal.token.PathTokenAppender;
import com.jayway.jsonpath.internal.token.PathTokenFactory;
import com.jayway.jsonpath.internal.token.RootPathToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
@ -22,8 +21,6 @@ import static java.util.Arrays.asList;
public class PathCompiler {
private static final Logger logger = LoggerFactory.getLogger(PathCompiler.class);
private static final char DOC_CONTEXT = '$';
private static final char EVAL_CONTEXT = '@';
private static final char OPEN_SQUARE_BRACKET = '[';
@ -39,6 +36,7 @@ public class PathCompiler {
private static final char MINUS = '-';
private static final char ESCAPE = '\\';
private static final char TICK = '\'';
private static final char FUNCTION = '%';
private static final Cache cache = new Cache(200);
@ -131,12 +129,42 @@ public class PathCompiler {
case WILDCARD:
return readWildCardToken(appender) ||
fail("Could not parse token at position " + path.position);
case FUNCTION:
return readFunctionToken(appender) ||
fail("Could not parse token at position " + path.position);
default:
return readPropertyToken(appender) ||
fail("Could not parse token at position " + path.position);
}
}
//
// $function()
//
private boolean readFunctionToken(PathTokenAppender appender) {
if (path.currentCharIs(OPEN_SQUARE_BRACKET) || path.currentCharIs(WILDCARD) || path.currentCharIs(PERIOD) || path.currentCharIs(SPACE)) {
return false;
}
int startPosition = path.position;
int readPosition = startPosition;
int endPosition = 0;
while (path.inBounds(readPosition)) {
char c = path.charAt(readPosition);
if (c == OPEN_BRACKET && path.nextSignificantCharIs(readPosition, CLOSE_BRACKET)) {
endPosition = path.indexOfNextSignificantChar(readPosition, CLOSE_BRACKET);
break;
}
readPosition++;
}
path.setPosition(endPosition);
String function = path.subSequence(startPosition, endPosition + 1).toString();
appender.appendPathToken(PathTokenFactory.createFunctionPathToken(function));
return path.currentIsTail();
}
//
// .
//
@ -372,10 +400,7 @@ public class PathCompiler {
while (path.inBounds(readPosition)) {
char c = path.charAt(readPosition);
if (c == CLOSE_SQUARE_BRACKET) {
if (inProperty) {
throw new InvalidPathException("Expected property to be closed at position " + readPosition);
}
if (c == CLOSE_SQUARE_BRACKET && !inProperty) {
break;
} else if (c == TICK) {
if (inProperty) {
@ -571,6 +596,3 @@ public class PathCompiler {
}
}
}

71
json-path/src/main/java/com/jayway/jsonpath/internal/function/FunctionFactory.java

@ -0,0 +1,71 @@
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.Function;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.function.numeric.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Implements a factory that given a name of the function will return the Function implementation, or null
* if the value is not obtained.
*
* Leverages the function's name in order to determine which function to execute which is maintained internally
* here via a static map
*
* Created by mattg on 6/27/15.
*/
public class FunctionFactory {
public static final Map<String, Class> FUNCTIONS;
static {
// New functions should be added here and ensure the name is not overridden
Map<String, Class> map = new HashMap<String, Class>();
// Math Functions
map.put("avg", Average.class);
map.put("stddev", StandardDeviation.class);
map.put("sum", Sum.class);
map.put("min", Min.class);
map.put("max", Max.class);
// JSON Entity Functions
map.put("length", Length.class);
FUNCTIONS = Collections.unmodifiableMap(map);
}
/**
* Either provides a pass thru function when the function cannot be properly mapped or otherwise returns the function
* implementation based on the name using the internal FUNCTION map
*
* @see #FUNCTIONS
* @see Function
*
* @param name
* The name of the function
*
* @return
* The implementation of a function
*
* @throws InvalidPathException
*/
public static Function newFunction(String name) throws InvalidPathException {
Function result = new PassthruFunction();
if (null != name && FUNCTIONS.containsKey(name) && Function.class.isAssignableFrom(FUNCTIONS.get(name))) {
try {
result = (Function)FUNCTIONS.get(name).newInstance();
} catch (InstantiationException e) {
throw new InvalidPathException("Function of name: " + name + " cannot be created", e);
} catch (IllegalAccessException e) {
throw new InvalidPathException("Function of name: " + name + " cannot be created", e);
}
}
return result;
}
}

26
json-path/src/main/java/com/jayway/jsonpath/internal/function/Length.java

@ -0,0 +1,26 @@
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.Function;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import net.minidev.json.JSONArray;
import java.util.*;
/**
* Provides the length of a JSONArray Object
*
* Created by mattg on 6/26/15.
*/
public class Length implements Function {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx) {
if(ctx.configuration().jsonProvider().isArray(model)){
return ctx.configuration().jsonProvider().length(model);
} else if(ctx.configuration().jsonProvider().isMap(model)){
return ctx.configuration().jsonProvider().length(model);
}
return null;
}
}

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

@ -0,0 +1,18 @@
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.Function;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
/**
* Defines the default behavior which is to return the model that is provided as input as output
*
* Created by mattg on 6/26/15.
*/
public class PassthruFunction implements Function {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx) {
return model;
}
}

55
json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/AbstractAggregation.java

@ -0,0 +1,55 @@
package com.jayway.jsonpath.internal.function.numeric;
import com.jayway.jsonpath.Function;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import net.minidev.json.JSONArray;
import java.util.Iterator;
/**
* Defines the pattern for processing numerical values via an abstract implementation that iterates over the collection
* of JSONArray entities and verifies that each is a numerical value and then passes that along the abstract methods
*
*
* Created by mattg on 6/26/15.
*/
public abstract class AbstractAggregation implements Function {
/**
* Defines the next value in the array to the mathmatical function
*
* @param value
* The numerical value to process next
*/
protected abstract void next(Number value);
/**
* Obtains the value generated via the series of next value calls
*
* @return
* A numerical answer based on the input value provided
*/
protected abstract Number getValue();
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx) {
if(ctx.configuration().jsonProvider().isArray(model)){
Iterable<?> objects = ctx.configuration().jsonProvider().toIterable(model);
for (Object obj : objects) {
// Object unwraped = ctx.configuration().jsonProvider().unwrap(obj);
// if (unwraped instanceof Number) {
// Number value = (Number) unwraped;
// next(value);
// }
if (obj instanceof Number) {
Number value = (Number) obj;
next(value);
}
}
return getValue();
}
return null;
}
}

26
json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Average.java

@ -0,0 +1,26 @@
package com.jayway.jsonpath.internal.function.numeric;
/**
* Provides the average of a series of numbers in a JSONArray
*
* Created by mattg on 6/26/15.
*/
public class Average extends AbstractAggregation {
private Double summation = 0d;
private Double count = 0d;
@Override
protected void next(Number value) {
count++;
summation += value.doubleValue();
}
@Override
protected Number getValue() {
if (count != 0d) {
return summation / count;
}
return 0d;
}
}

22
json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Max.java

@ -0,0 +1,22 @@
package com.jayway.jsonpath.internal.function.numeric;
/**
* Defines the summation of a series of JSONArray numerical values
*
* Created by mattg on 6/26/15.
*/
public class Max extends AbstractAggregation {
private Double max = Double.MIN_VALUE;
@Override
protected void next(Number value) {
if (max < value.doubleValue()) {
max = value.doubleValue();
}
}
@Override
protected Number getValue() {
return max;
}
}

22
json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java

@ -0,0 +1,22 @@
package com.jayway.jsonpath.internal.function.numeric;
/**
* Defines the summation of a series of JSONArray numerical values
*
* Created by mattg on 6/26/15.
*/
public class Min extends AbstractAggregation {
private Double min = Double.MAX_VALUE;
@Override
protected void next(Number value) {
if (min > value.doubleValue()) {
min = value.doubleValue();
}
}
@Override
protected Number getValue() {
return min;
}
}

24
json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/StandardDeviation.java

@ -0,0 +1,24 @@
package com.jayway.jsonpath.internal.function.numeric;
/**
* Provides the standard deviation of a series of numbers
*
* Created by mattg on 6/27/15.
*/
public class StandardDeviation extends AbstractAggregation {
private Double sumSq = 0d;
private Double sum = 0d;
private Double count = 0d;
@Override
protected void next(Number value) {
sum += value.doubleValue();
sumSq += value.doubleValue() * value.doubleValue();
count++;
}
@Override
protected Number getValue() {
return Math.sqrt(sumSq/count - sum*sum/count/count);
}
}

20
json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Sum.java

@ -0,0 +1,20 @@
package com.jayway.jsonpath.internal.function.numeric;
/**
* Defines the summation of a series of JSONArray numerical values
*
* Created by mattg on 6/26/15.
*/
public class Sum extends AbstractAggregation {
private Double summation = 0d;
@Override
protected void next(Number value) {
summation += value.doubleValue();
}
@Override
protected Number getValue() {
return summation;
}
}

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

@ -0,0 +1,56 @@
package com.jayway.jsonpath.internal.token;
import com.jayway.jsonpath.Function;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.FunctionFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Token representing a Function call to one of the functions produced via the FunctionFactory
*
* @see FunctionFactory
*
* Created by mattg on 6/27/15.
*/
public class FunctionPathToken extends PathToken {
private final String functionName;
private final String pathFragment;
public FunctionPathToken(String pathFragment) {
this.pathFragment = pathFragment;
Matcher matcher = Pattern.compile(".*?\\%(\\w+)\\(.*?").matcher(pathFragment);
if (matcher.matches()) {
functionName = matcher.group(1);
}
else {
// We'll end up throwing an error from the factory when we get that far
functionName = null;
}
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
Function function = FunctionFactory.newFunction(functionName);
Object result = function.invoke(currentPath, parent, model, ctx);
ctx.addResult(currentPath, parent, result);
}
/**
* 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
*/
@Override
public boolean isTokenDefinite() {
return true;
}
@Override
public String getPathFragment() {
return pathFragment;
}
}

14
json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java

@ -14,11 +14,17 @@
*/
package com.jayway.jsonpath.internal.token;
import com.jayway.jsonpath.Function;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.function.FunctionFactory;
import com.jayway.jsonpath.internal.function.numeric.Average;
import com.jayway.jsonpath.internal.function.Length;
import com.jayway.jsonpath.internal.function.PassthruFunction;
import com.jayway.jsonpath.internal.function.numeric.Sum;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.List;
@ -59,7 +65,6 @@ public abstract class PathToken {
} else {
throw new PathNotFoundException("No results for path: " + evalPath);
}
}
} else {
if (! (isUpstreamDefinite() && isTokenDefinite()) &&
@ -77,7 +82,8 @@ public abstract class PathToken {
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP;
if (isLeaf()) {
ctx.addResult(evalPath, pathRef, propertyVal);
} else {
}
else {
next().evaluate(evalPath, pathRef, propertyVal, ctx);
}
} else {
@ -204,6 +210,10 @@ public abstract class PathToken {
return super.equals(obj);
}
public void invoke(Function function, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
ctx.addResult(currentPath, parent, function.invoke(currentPath, parent, model, ctx));
}
public abstract void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx);
public abstract boolean isTokenDefinite();

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

@ -44,4 +44,8 @@ public class PathTokenFactory {
public static PathToken createPredicatePathToken(Predicate predicate) {
return new PredicatePathToken(predicate);
}
public static PathToken createFunctionPathToken(String function) {
return new FunctionPathToken((function));
}
}

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

@ -64,7 +64,12 @@ public class PropertyPathToken extends PathToken {
if (! isUpstreamDefinite()) {
return;
} else {
throw new PathNotFoundException("Property " + getPathFragment() + " not found in path " + currentPath);
String m = model == null ? "null" : model.getClass().getName();
throw new PathNotFoundException(String.format(
"Expected to find an object with property %s in path %s but found '%s'. " +
"This is not a json object according to the JsonProvider: '%s'.",
getPathFragment(), currentPath, m, ctx.configuration().jsonProvider().getClass().getName()));
}
}

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

@ -73,5 +73,7 @@ public class RootPathToken extends PathToken {
return true;
}
public boolean isFunctionPath() {
return (tail instanceof FunctionPathToken);
}
}

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

@ -4,9 +4,11 @@ import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.spi.json.GsonJsonProvider;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider;
import com.jayway.jsonpath.spi.mapper.GsonMappingProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.internal.token.PredicateContextImpl;
import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider;
import java.util.HashMap;
@ -30,7 +32,11 @@ public class BaseTest {
.jsonProvider(new JacksonJsonNodeJsonProvider())
.build();
public static final Configuration JSON_SMART_CONFIGURATION = Configuration.defaultConfiguration();
public static final Configuration JSON_SMART_CONFIGURATION = Configuration
.builder()
.mappingProvider(new JsonSmartMappingProvider())
.jsonProvider(new JsonSmartJsonProvider())
.build();
public static final String JSON_BOOK_DOCUMENT =
"{ " +

56
json-path/src/test/java/com/jayway/jsonpath/Configurations.java

@ -0,0 +1,56 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.spi.json.GsonJsonProvider;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider;
import com.jayway.jsonpath.spi.mapper.GsonMappingProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider;
import java.util.Arrays;
public class Configurations {
public static final Configuration GSON_CONFIGURATION = Configuration
.builder()
.mappingProvider(new GsonMappingProvider())
.jsonProvider(new GsonJsonProvider())
.build();
public static final Configuration JACKSON_CONFIGURATION = Configuration
.builder()
.mappingProvider(new JacksonMappingProvider())
.jsonProvider(new JacksonJsonProvider())
.build();
public static final Configuration JACKSON_JSON_NODE_CONFIGURATION = Configuration
.builder()
.mappingProvider(new JacksonMappingProvider())
.jsonProvider(new JacksonJsonNodeJsonProvider())
.build();
public static final Configuration JSON_SMART_CONFIGURATION = Configuration
.builder()
.mappingProvider(new JsonSmartMappingProvider())
.jsonProvider(new JsonSmartJsonProvider())
.build();
public static Iterable<Configuration> configurations() {
return Arrays.asList(
JSON_SMART_CONFIGURATION
,GSON_CONFIGURATION
,JACKSON_CONFIGURATION
,JACKSON_JSON_NODE_CONFIGURATION
);
}
public static Iterable<Configuration> objectMappingConfigurations() {
return Arrays.asList(
GSON_CONFIGURATION
,JACKSON_CONFIGURATION
,JACKSON_JSON_NODE_CONFIGURATION
);
}
}

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

@ -11,8 +11,6 @@ import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
import static com.jayway.jsonpath.Filter.parse;
import static java.lang.System.out;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
public class FilterTest extends BaseTest {
@ -473,74 +471,116 @@ public class FilterTest extends BaseTest {
@Test
public void a_gte_filter_can_be_serialized() {
System.out.println(filter(where("a").gte(1)).toString());
String filter = filter(where("a").gte(1)).toString();
String parsed = parse("[?(@['a'] >= 1)]").toString();
assertThat(filter(where("a").gte(1)).toString()).isEqualTo(parse("[?(@['a'] >= 1)]").toString());
assertThat(filter).isEqualTo(parse(parsed).toString());
}
@Test
public void a_lte_filter_can_be_serialized() {
assertThat(filter(where("a").lte(1)).toString()).isEqualTo("[?(@['a'] <= 1)]");
String filter = filter(where("a").lte(1)).toString();
String parsed = parse("[?(@['a'] <= 1)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_eq_filter_can_be_serialized() {
assertThat(filter(where("a").eq(1)).toString()).isEqualTo("[?(@['a'] == 1)]");
String filter = filter(where("a").eq(1)).toString();
String parsed = parse("[?(@['a'] == 1)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_ne_filter_can_be_serialized() {
assertThat(filter(where("a").ne(1)).toString()).isEqualTo("[?(@['a'] != 1)]");
String filter = filter(where("a").ne(1)).toString();
String parsed = parse("[?(@['a'] != 1)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_lt_filter_can_be_serialized() {
assertThat(filter(where("a").lt(1)).toString()).isEqualTo("[?(@['a'] < 1)]");
String filter = filter(where("a").lt(1)).toString();
String parsed = parse("[?(@['a'] < 1)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_gt_filter_can_be_serialized() {
assertThat(filter(where("a").gt(1)).toString()).isEqualTo("[?(@['a'] > 1)]");
}
@Test
public void a_regex_filter_can_be_serialized() {
assertThat(filter(where("a").regex(Pattern.compile("/.*?/i"))).toString()).isEqualTo("[?(@['a'] =~ /.*?/i)]");
String filter = filter(where("a").gt(1)).toString();
String parsed = parse("[?(@['a'] > 1)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_nin_filter_can_be_serialized() {
assertThat(filter(where("a").nin(1)).toString()).isEqualTo("[?(@['a'] ¦NIN¦ [1])]");
String filter = filter(where("a").nin(1)).toString();
String parsed = parse("[?(@['a'] ¦NIN¦ [1])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_in_filter_can_be_serialized() {
assertThat(filter(where("a").in("a")).toString()).isEqualTo("[?(@['a'] ¦IN¦ ['a'])]");
String filter = filter(where("a").in("a")).toString();
String parsed = parse("[?(@['a'] ¦IN¦ ['a'])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_contains_filter_can_be_serialized() {
assertThat(filter(where("a").contains("a")).toString()).isEqualTo("[?(@['a'] ¦CONTAINS¦ 'a')]");
String filter = filter(where("a").contains("a")).toString();
String parsed = parse("[?(@['a'] ¦CONTAINS¦ 'a')]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_all_filter_can_be_serialized() {
assertThat(filter(where("a").all("a", "b")).toString()).isEqualTo("[?(@['a'] ¦ALL¦ ['a','b'])]");
String filter = filter(where("a").all("a", "b")).toString();
String parsed = parse("[?(@['a'] ¦ALL¦ ['a','b'])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_size_filter_can_be_serialized() {
assertThat(filter(where("a").size(5)).toString()).isEqualTo("[?(@['a'] ¦SIZE¦ 5)]");
String filter = filter(where("a").size(5)).toString();
String parsed = parse("[?(@['a'] ¦SIZE¦ 5)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_exists_filter_can_be_serialized() {
assertThat(filter(where("a").exists(true)).toString()).isEqualTo("[?(@['a'])]");
String filter = filter(where("a").exists(true)).toString();
String parsed = parse("[?(@['a'])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_not_exists_filter_can_be_serialized() {
assertThat(filter(where("a").exists(false)).toString()).isEqualTo("[?(!@['a'])]");
String filter = filter(where("a").exists(false)).toString();
String parsed = parse("[?(!@['a'])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
@ -557,22 +597,43 @@ public class FilterTest extends BaseTest {
@Test
public void a_not_empty_filter_can_be_serialized() {
assertThat(filter(where("a").notEmpty()).toString()).isEqualTo("[?(@['a'] ¦NOT_EMPTY¦)]");
String filter = filter(where("a").notEmpty()).toString();
String parsed = parse("[?(@['a'] ¦NOT_EMPTY¦)]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void and_filter_can_be_serialized() {
assertThat(filter(where("a").eq(1).and("b").eq(2)).toString()).isEqualTo("[?(@['a'] == 1 && @['b'] == 2)]");
String filter = filter(where("a").eq(1).and("b").eq(2)).toString();
String parsed = parse("[?(@['b'] == 2 && @['a'] == 1)]").toString(); //FIXME: criteria are reversed
assertThat(filter).isEqualTo(parsed);
}
@Test
public void in_string_filter_can_be_serialized() {
assertThat(filter(where("a").in("1","2")).toString()).isEqualTo("[?(@['a'] ¦IN¦ ['1','2'])]");
String filter = filter(where("a").in("1","2")).toString();
String parsed = parse("[?(@['a'] ¦IN¦ ['1','2'])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_deep_path_filter_can_be_serialized() {
assertThat(filter(where("a.b.c").in("1","2")).toString()).isEqualTo("[?(@['a']['b']['c'] ¦IN¦ ['1','2'])]");
String filter = filter(where("a.b.c").in("1", "2")).toString();
String parsed = parse("[?(@['a']['b']['c'] ¦IN¦ ['1','2'])]").toString();
assertThat(filter).isEqualTo(parsed);
}
@Test
public void a_regex_filter_can_be_serialized() {
assertThat(filter(where("a").regex(Pattern.compile("/.*?/i"))).toString()).isEqualTo("[?(@['a'] =~ /.*?/i)]");
}
@Test
@ -585,9 +646,13 @@ public class FilterTest extends BaseTest {
Filter a = filter(where("a").eq(1));
Filter b = filter(where("b").eq(2));
Filter c = a.and(b);
Filter c = b.and(a);
String filter = c.toString();
String parsed = parse("[?(@['a'] == 1 && @['b'] == 2)]").toString();
assertThat(c.toString()).isEqualTo("[?(@['a'] == 1 && @['b'] == 2)]");
assertThat(filter).isEqualTo(parsed);
}
@Test
@ -595,26 +660,11 @@ public class FilterTest extends BaseTest {
Filter a = filter(where("a").eq(1));
Filter b = filter(where("b").eq(2));
Filter c = a.or(b);
assertThat(c.toString()).isEqualTo("[?(@['a'] == 1 || @['b'] == 2)]");
}
@Test
public void a_____() {
// :2
// 1:2
// -2:
//2:
Filter c = b.or(a);
out.println(asList(":2".split(":"))); //[, 2]
out.println(asList("1:2".split(":"))); //[1, 2]
out.println(asList("-2:".split(":"))); //[-2]
out.println(asList("2:".split(":"))); //[2]
out.println(asList(":2".split(":")).get(0).equals("")); //true
String filter = c.toString();
String parsed = parse("[?(@['a'] == 1 || @['b'] == 2)]").toString();
assertThat(filter).isEqualTo(parsed);
}
}

11
json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java

@ -152,10 +152,15 @@ public class InlineFilterTest extends BaseTest {
ints.add(3);
List<Integer> notNull = JsonPath.parse(ints).read("$[?(@)]");
assertThat(notNull).containsExactly(0,1,2,3);
List<Integer> hits = JsonPath.parse(ints).read("$[?(@)]");
assertThat(hits).containsExactly(0,1,null,2,3);
hits = JsonPath.parse(ints).read("$[?(@ != null)]");
assertThat(hits).containsExactly(0,1,2,3);
List<Integer> isNull = JsonPath.parse(ints).read("$[?(!@)]");
assertThat(isNull).containsExactly(new Integer[]{null});
assertThat(isNull).containsExactly(new Integer[]{});
assertThat(isNull).containsExactly(new Integer[]{});
}
}

76
json-path/src/test/java/com/jayway/jsonpath/JsonProviderTest.java

@ -1,6 +1,8 @@
package com.jayway.jsonpath;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.List;
@ -8,84 +10,38 @@ import java.util.List;
import static com.jayway.jsonpath.JsonPath.using;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(Parameterized.class)
public class JsonProviderTest extends BaseTest {
private static final String JSON =
"[" +
"{\n" +
" \"foo\" : \"foo0\",\n" +
" \"bar\" : 0,\n" +
" \"baz\" : true,\n" +
" \"gen\" : {\"prop\" : \"yepp0\"}" +
"}," +
"{\n" +
" \"foo\" : \"foo1\",\n" +
" \"bar\" : 1,\n" +
" \"baz\" : true,\n" +
" \"gen\" : {\"prop\" : \"yepp1\"}" +
"}," +
"{\n" +
" \"foo\" : \"foo2\",\n" +
" \"bar\" : 2,\n" +
" \"baz\" : true,\n" +
" \"gen\" : {\"prop\" : \"yepp2\"}" +
"}" +
"]";
private final Configuration conf;
@Test
public void strings_are_unwrapped() {
assertThat(using(JACKSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.string-property", String.class)).isEqualTo("string-value");
assertThat(using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.string-property", String.class)).isEqualTo("string-value");
assertThat(using(JSON_SMART_CONFIGURATION).parse(JSON_DOCUMENT).read("$.string-property", String.class)).isEqualTo("string-value");
assertThat(using(GSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.string-property", String.class)).isEqualTo("string-value");
public JsonProviderTest(Configuration conf) {
this.conf = conf;
}
@Test
public void integers_are_unwrapped() {
assertThat(using(JACKSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", Integer.class)).isEqualTo(Integer.MAX_VALUE);
assertThat(using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", Integer.class)).isEqualTo(Integer.MAX_VALUE);
assertThat(using(JSON_SMART_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", Integer.class)).isEqualTo(Integer.MAX_VALUE);
assertThat(using(GSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", Integer.class)).isEqualTo(Integer.MAX_VALUE);
@Parameterized.Parameters
public static Iterable<Configuration> configurations() {
return Configurations.configurations();
}
@Test
public void ints_are_unwrapped() {
assertThat(using(JACKSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", int.class)).isEqualTo(Integer.MAX_VALUE);
assertThat(using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", int.class)).isEqualTo(Integer.MAX_VALUE);
assertThat(using(JSON_SMART_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", int.class)).isEqualTo(Integer.MAX_VALUE);
assertThat(using(GSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", int.class)).isEqualTo(Integer.MAX_VALUE);
public void strings_are_unwrapped() {
assertThat(using(conf).parse(JSON_DOCUMENT).read("$.string-property", String.class)).isEqualTo("string-value");
}
@Test
public void list_of_numbers() {
TypeRef<List<Double>> typeRef = new TypeRef<List<Double>>() {};
assertThat(using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.store.book[*].display-price", typeRef)).containsExactly(8.95D, 12.99D, 8.99D, 22.99D);
assertThat(using(JACKSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.store.book[*].display-price", typeRef)).containsExactly(8.95D, 12.99D, 8.99D, 22.99D);
assertThat(using(GSON_CONFIGURATION).parse(JSON_DOCUMENT).read("$.store.book[*].display-price", typeRef)).containsExactly(8.95D, 12.99D, 8.99D, 22.99D);
public void integers_are_unwrapped() {
assertThat(using(conf).parse(JSON_DOCUMENT).read("$.int-max-property", Integer.class)).isEqualTo(Integer.MAX_VALUE);
}
@Test
public void test_type_ref() throws IOException {
TypeRef<List<FooBarBaz<Sub>>> typeRef = new TypeRef<List<FooBarBaz<Sub>>>() {};
assertThat(using(JACKSON_CONFIGURATION).parse(JSON).read("$", typeRef)).extracting("foo").containsExactly("foo0", "foo1", "foo2");
assertThat(using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON).read("$", typeRef)).extracting("foo").containsExactly("foo0", "foo1", "foo2");
assertThat(using(GSON_CONFIGURATION).parse(JSON).read("$", typeRef)).extracting("foo").containsExactly("foo0", "foo1", "foo2");
public void ints_are_unwrapped() {
assertThat(using(conf).parse(JSON_DOCUMENT).read("$.int-max-property", int.class)).isEqualTo(Integer.MAX_VALUE);
}
public static class FooBarBaz<T> {
public T gen;
public String foo;
public Long bar;
public boolean baz;
}
public static class Sub {
public String prop;
}
}

75
json-path/src/test/java/com/jayway/jsonpath/JsonProviderTestObjectMapping.java

@ -0,0 +1,75 @@
package com.jayway.jsonpath;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.List;
import static com.jayway.jsonpath.JsonPath.using;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(Parameterized.class)
public class JsonProviderTestObjectMapping extends BaseTest {
private static final String JSON =
"[" +
"{\n" +
" \"foo\" : \"foo0\",\n" +
" \"bar\" : 0,\n" +
" \"baz\" : true,\n" +
" \"gen\" : {\"prop\" : \"yepp0\"}" +
"}," +
"{\n" +
" \"foo\" : \"foo1\",\n" +
" \"bar\" : 1,\n" +
" \"baz\" : true,\n" +
" \"gen\" : {\"prop\" : \"yepp1\"}" +
"}," +
"{\n" +
" \"foo\" : \"foo2\",\n" +
" \"bar\" : 2,\n" +
" \"baz\" : true,\n" +
" \"gen\" : {\"prop\" : \"yepp2\"}" +
"}" +
"]";
private final Configuration conf;
public JsonProviderTestObjectMapping(Configuration conf) {
this.conf = conf;
}
@Parameterized.Parameters
public static Iterable<Configuration> configurations() {
return Configurations.objectMappingConfigurations();
}
@Test
public void list_of_numbers() {
TypeRef<List<Double>> typeRef = new TypeRef<List<Double>>() {};
assertThat(using(conf).parse(JSON_DOCUMENT).read("$.store.book[*].display-price", typeRef)).containsExactly(8.95D, 12.99D, 8.99D, 22.99D);
}
@Test
public void test_type_ref() throws IOException {
TypeRef<List<FooBarBaz<Sub>>> typeRef = new TypeRef<List<FooBarBaz<Sub>>>() {};
assertThat(using(conf).parse(JSON).read("$", typeRef)).extracting("foo").containsExactly("foo0", "foo1", "foo2");
}
public static class FooBarBaz<T> {
public T gen;
public String foo;
public Long bar;
public boolean baz;
}
public static class Sub {
public String prop;
}
}

45
json-path/src/test/java/com/jayway/jsonpath/functions/BaseFunctionTest.java

@ -0,0 +1,45 @@
package com.jayway.jsonpath.functions;
import com.jayway.jsonpath.Configuration;
import java.io.IOException;
import java.util.Scanner;
import static com.jayway.jsonpath.JsonPath.using;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Created by mattg on 6/27/15.
*/
public class BaseFunctionTest {
protected static final String NUMBER_SERIES = "{\"numbers\" : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}";
protected static final String TEXT_SERIES = "{\"text\" : [ \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" ]}";
/**
* Verify the function returns the correct result based on the input expectedValue
*
* @param pathExpr
* The path expression to execute
*
* @param json
* The json document (actual content) to parse
*
* @param expectedValue
* The expected value to be returned from the test
*/
protected void verifyFunction(Configuration conf, String pathExpr, String json, Object expectedValue) {
Object result = using(conf).parse(json).read(pathExpr);
assertThat(result).isEqualTo(expectedValue);
}
protected void verifyMathFunction(Configuration conf, String pathExpr, Object expectedValue) {
verifyFunction(conf, pathExpr, NUMBER_SERIES, expectedValue);
}
protected String getResourceAsText(String resourceName) throws IOException {
return new Scanner(BaseFunctionTest.class.getResourceAsStream(resourceName), "UTF-8").useDelimiter("\\A").next();
}
}

105
json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java

@ -0,0 +1,105 @@
package com.jayway.jsonpath.functions;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Configurations;
import net.minidev.json.JSONArray;
import org.junit.Test;
/**
* Verifies methods that are helper implementations of functions for manipulating JSON entities, i.e.
* length, etc.
*
* Created by mattg on 6/27/15.
*/
public class JSONEntityFunctionTest extends BaseFunctionTest {
private static final String BATCH_JSON = "{\n" +
" \"batches\": {\n" +
" \"minBatchSize\": 10,\n" +
" \"results\": [\n" +
" {\n" +
" \"productId\": 23,\n" +
" \"values\": [\n" +
" 2,\n" +
" 45,\n" +
" 34,\n" +
" 23,\n" +
" 3,\n" +
" 5,\n" +
" 4,\n" +
" 3,\n" +
" 2,\n" +
" 1,\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"productId\": 23,\n" +
" \"values\": [\n" +
" 52,\n" +
" 3,\n" +
" 12,\n" +
" 11,\n" +
" 18,\n" +
" 22,\n" +
" 1\n" +
" ]\n" +
" }\n" +
" ]\n" +
" }\n" +
"}";
private Configuration conf = Configurations.JSON_SMART_CONFIGURATION;
@Test
public void testLengthOfTextArray() {
// The length of JSONArray is an integer
System.out.println(TEXT_SERIES);
verifyFunction(conf, "$['text'].%length()", TEXT_SERIES, 6);
}
@Test
public void testLengthOfNumberArray() {
// The length of JSONArray is an integer
verifyFunction(conf, "$.numbers.%length()", NUMBER_SERIES, 10);
}
@Test
public void testLengthOfStructure() {
verifyFunction(conf, "$.batches.%length()", BATCH_JSON, 2);
}
/**
* The fictitious use-case/story - is we have a collection of batches with values indicating some quality metric.
* We want to determine the average of the values for only the batch's values where the number of items in the batch
* is greater than the min batch size which is encoded in the JSON document.
*
* We use the length function in the predicate to determine the number of values in each batch and then for those
* batches where the count is greater than min we calculate the average batch value.
*
* Its completely contrived example, however, this test exercises functions within predicates.
*/
@Test
public void testPredicateWithFunctionCallSingleMatch() {
String path = "$.batches.results[?(@.values.%length() >= $.batches.minBatchSize)].values.%avg()";
// Its an array because in some use-cases the min size might match more than one batch and thus we'll get
// the average out for each collection
JSONArray values = new JSONArray();
values.add(12.2d);
verifyFunction(conf, path, BATCH_JSON, values);
}
@Test
public void testPredicateWithFunctionCallTwoMatches() {
String path = "$.batches.results[?(@.values.%length() >= 3)].values.%avg()";
// Its an array because in some use-cases the min size might match more than one batch and thus we'll get
// the average out for each collection
JSONArray values = new JSONArray();
values.add(12.2d);
values.add(17d);
verifyFunction(conf, path, BATCH_JSON, values);
}
}

90
json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java

@ -0,0 +1,90 @@
package com.jayway.jsonpath.functions;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Configurations;
import com.jayway.jsonpath.JsonPath;
import net.minidev.json.JSONArray;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import static com.jayway.jsonpath.Configurations.*;
import static com.jayway.jsonpath.JsonPath.using;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.runners.Parameterized.Parameters;
/**
* Defines functional tests around executing:
*
* - sum
* - avg
* - stddev
*
* for each of the above, executes the test and verifies that the results are as expected based on a static input
* and static output.
*
* Created by mattg on 6/26/15.
*/
@RunWith(Parameterized.class)
public class NumericFunctionTest extends BaseFunctionTest {
private static final Logger logger = LoggerFactory.getLogger(NumericFunctionTest.class);
private Configuration conf = Configurations.GSON_CONFIGURATION;
public NumericFunctionTest(Configuration conf) {
logger.debug("Testing with configuration {}", conf.getClass().getName());
this.conf = conf;
}
@Parameters
public static Iterable<Configuration> configurations() {
return Configurations.configurations();
}
@Test
public void testAverageOfDoubles() {
verifyMathFunction(conf, "$.numbers.%avg()", 5.5);
}
@Test
public void testSumOfDouble() {
verifyMathFunction(conf, "$.numbers.%sum()", (10d * (10d + 1d)) / 2d);
}
@Test
public void testMaxOfDouble() {
verifyMathFunction(conf, "$.numbers.%max()", 10d);
}
@Test
public void testMinOfDouble() {
verifyMathFunction(conf, "$.numbers.%min()", 1d);
}
@Test
public void testStdDevOfDouble() {
verifyMathFunction(conf, "$.numbers.%stddev()", 2.8722813232690143d);
}
/**
* Expect that for an invalid function name we'll get back the original input to the function
*/
// @Test
// @Ignore
// public void testInvalidFunctionNameNegative() {
// JSONArray numberSeries = new JSONArray();
// numberSeries.addAll(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// assertThat(using(conf).parse(NUMBER_SERIES).read("$.numbers.%foo()")).isEqualTo(numberSeries);
// }
}

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

@ -2,9 +2,7 @@ package com.jayway.jsonpath.old;
import com.jayway.jsonpath.BaseTest;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Criteria;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.InvalidCriteriaException;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.spi.json.JsonProvider;
@ -201,8 +199,8 @@ public class FilterTest extends BaseTest {
assertTrue(filter(where("foo").exists(true)).apply(createPredicateContext(check)));
assertFalse(filter(where("foo").exists(false)).apply(createPredicateContext(check)));
assertTrue(filter(where("foo_null").exists(false)).apply(createPredicateContext(check)));
assertFalse(filter(where("foo_null").exists(true)).apply(createPredicateContext(check)));
assertTrue(filter(where("foo_null").exists(true)).apply(createPredicateContext(check)));
assertFalse(filter(where("foo_null").exists(false)).apply(createPredicateContext(check)));
assertTrue(filter(where("bar").exists(false)).apply(createPredicateContext(check)));
assertFalse(filter(where("bar").exists(true)).apply(createPredicateContext(check)));

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

@ -1,5 +1,6 @@
package com.jayway.jsonpath.old;
import com.google.gson.JsonObject;
import com.jayway.jsonpath.BaseTest;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
@ -33,6 +34,7 @@ import static com.jayway.jsonpath.Filter.filter;
import static com.jayway.jsonpath.JsonPath.read;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
@ -175,7 +177,7 @@ public class IssuesTest extends BaseTest {
List<String> result = read(json, "$[?(@.compatible == true)].sku");
Assertions.assertThat(result).containsExactly("SKU-005", "SKU-003");
assertThat(result).containsExactly("SKU-005", "SKU-003");
}
@ -259,7 +261,7 @@ public class IssuesTest extends BaseTest {
public void issue_22b() throws Exception {
String json = "{\"a\":[{\"b\":1,\"c\":2},{\"b\":5,\"c\":2}]}";
List<Object> res = JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.DEFAULT_PATH_LEAF_TO_NULL)).parse(json).read("a[?(@.b==5)].d");
Assertions.assertThat(res).hasSize(1).containsNull();
assertThat(res).hasSize(1).containsNull();
}
@Test(expected = PathNotFoundException.class)
@ -334,8 +336,8 @@ public class IssuesTest extends BaseTest {
List<Map<String, Object>> result = read(json, "$.store.book[?(@.author.age == 36)]");
Assertions.assertThat(result).hasSize(1);
Assertions.assertThat(result.get(0)).containsEntry("title", "Sayings of the Century");
assertThat(result).hasSize(1);
assertThat(result.get(0)).containsEntry("title", "Sayings of the Century");
}
@Test
@ -381,7 +383,7 @@ public class IssuesTest extends BaseTest {
List<Map<String, String>> result = read(json, "$.list[?(@.name == 'My (String)')]");
Assertions.assertThat(result).containsExactly(Collections.singletonMap("name", "My (String)"));
assertThat(result).containsExactly(Collections.singletonMap("name", "My (String)"));
}
@Test
@ -389,9 +391,9 @@ public class IssuesTest extends BaseTest {
String json = "{\"test\":null}";
Assertions.assertThat(read(json, "test")).isNull();
assertThat(read(json, "test")).isNull();
Assertions.assertThat(JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS)).parse(json).read("nonExistingProperty")).isNull();
assertThat(JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS)).parse(json).read("nonExistingProperty")).isNull();
try {
read(json, "nonExistingProperty");
@ -416,7 +418,7 @@ public class IssuesTest extends BaseTest {
public void issue_45() {
String json = "{\"rootkey\":{\"sub.key\":\"value\"}}";
Assertions.assertThat(read(json, "rootkey['sub.key']")).isEqualTo("value");
assertThat(read(json, "rootkey['sub.key']")).isEqualTo("value");
}
@Test
@ -426,14 +428,14 @@ public class IssuesTest extends BaseTest {
String json = "{\"a\": {}}";
Configuration configuration = Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS);
Assertions.assertThat(JsonPath.using(configuration).parse(json).read("a.x")).isNull();
assertThat(JsonPath.using(configuration).parse(json).read("a.x")).isNull();
try {
read(json, "a.x");
failBecauseExceptionWasNotThrown(PathNotFoundException.class);
} catch (PathNotFoundException e) {
Assertions.assertThat(e).hasMessage("No results for path: $['a']['x']");
assertThat(e).hasMessage("No results for path: $['a']['x']");
}
}
@ -449,7 +451,7 @@ public class IssuesTest extends BaseTest {
List<String> result = JsonPath.read(json, "$.a.*.b.*.c");
Assertions.assertThat(result).containsExactly("foo");
assertThat(result).containsExactly("foo");
}
@ -505,7 +507,7 @@ public class IssuesTest extends BaseTest {
List<String> problems = JsonPath.read(json, "$..narratives[?(@.lastRule==true)].message");
Assertions.assertThat(problems).containsExactly("Chain does not have a discovery event. Possible it was cut by the date that was picked", "No start transcoding events found");
assertThat(problems).containsExactly("Chain does not have a discovery event. Possible it was cut by the date that was picked", "No start transcoding events found");
}
//http://stackoverflow.com/questions/28596324/jsonpath-filtering-api
@ -567,7 +569,7 @@ public class IssuesTest extends BaseTest {
List<String> result = JsonPath.read(json, "$.logs[?(@.message == 'it\\'s here')].message");
Assertions.assertThat(result).containsExactly("it's here");
assertThat(result).containsExactly("it's here");
}
@Test
@ -600,7 +602,7 @@ public class IssuesTest extends BaseTest {
List<String> res = JsonPath.read(json, "$.c.*.url[2]");
Assertions.assertThat(res).containsExactly("url5");
assertThat(res).containsExactly("url5");
}
@Test
@ -614,7 +616,7 @@ public class IssuesTest extends BaseTest {
}
Thread.sleep(2000);
Assertions.assertThat(cache.size()).isEqualTo(200);
assertThat(cache.size()).isEqualTo(200);
}
@Test
@ -657,12 +659,12 @@ public class IssuesTest extends BaseTest {
cache.get("6");
Assertions.assertThat(cache.getSilent("6")).isNotNull();
Assertions.assertThat(cache.getSilent("5")).isNotNull();
Assertions.assertThat(cache.getSilent("4")).isNotNull();
Assertions.assertThat(cache.getSilent("3")).isNotNull();
Assertions.assertThat(cache.getSilent("2")).isNotNull();
Assertions.assertThat(cache.getSilent("1")).isNull();
assertThat(cache.getSilent("6")).isNotNull();
assertThat(cache.getSilent("5")).isNotNull();
assertThat(cache.getSilent("4")).isNotNull();
assertThat(cache.getSilent("3")).isNotNull();
assertThat(cache.getSilent("2")).isNotNull();
assertThat(cache.getSilent("1")).isNull();
}
@Test
@ -690,7 +692,7 @@ public class IssuesTest extends BaseTest {
List<String> categories = context.read("$..category", List.class);
Assertions.assertThat(categories).containsOnly("fiction");
assertThat(categories).containsOnly("fiction");
}
@ -736,10 +738,10 @@ public class IssuesTest extends BaseTest {
Filter parsed = Filter.parse(filterAsString);
Assertions.assertThat(orig.apply(createPredicateContext(match))).isTrue();
Assertions.assertThat(parsed.apply(createPredicateContext(match))).isTrue();
Assertions.assertThat(orig.apply(createPredicateContext(noMatch))).isFalse();
Assertions.assertThat(parsed.apply(createPredicateContext(noMatch))).isFalse();
assertThat(orig.apply(createPredicateContext(match))).isTrue();
assertThat(parsed.apply(createPredicateContext(match))).isTrue();
assertThat(orig.apply(createPredicateContext(noMatch))).isFalse();
assertThat(parsed.apply(createPredicateContext(noMatch))).isFalse();
}
private PredicateContext createPredicateContext(final Map<String, Integer> map){
@ -765,4 +767,124 @@ public class IssuesTest extends BaseTest {
}
};
}
@Test
public void issue_131() {
String json = "[\n" +
" {\n" +
" \"foo\": \"1\"\n" +
" },\n" +
" {\n" +
" \"foo\": null\n" +
" },\n" +
" {\n" +
" \"xxx\": null\n" +
" }\n" +
"]";
List<Map<String, String>> result = JsonPath.read(json, "$[?(@.foo)]");
assertThat(result).extracting("foo").containsExactly("1", null);
}
@Test
public void issue_131_2() {
String json = "[\n" +
" {\n" +
" \"foo\": { \"bar\" : \"0\"}\n" +
" },\n" +
" {\n" +
" \"foo\": null\n" +
" },\n" +
" {\n" +
" \"xxx\": null\n" +
" }\n" +
"]";
List<String> result = JsonPath.read(json, "$[?(@.foo != null)].foo.bar");
assertThat(result).containsExactly("0");
result = JsonPath.read(json, "$[?(@.foo.bar)].foo.bar");
assertThat(result).containsExactly("0");
}
@Test
public void issue_131_3() {
String json = "[\n" +
" 1,\n" +
" 2,\n" +
" {\n" +
" \"d\": {\n" +
" \"random\": null,\n" +
" \"date\": 1234\n" +
" },\n" +
" \"l\": \"filler\"\n" +
" }\n" +
"]";
List<Integer> result = JsonPath.read(json, "$[2]['d'][?(@.random)]['date']");
assertThat(result).containsExactly(1234);
}
//https://groups.google.com/forum/#!topic/jsonpath/Ojv8XF6LgqM
@Test
public void using_square_bracket_literal_path() {
String json = "{ \"valid key[@num = 2]\" : \"value\" }";
String result = JsonPath.read(json, "$['valid key[@num = 2]']");
Assertions.assertThat(result).isEqualTo("value");
}
@Test
public void issue_90() {
String json = "{\n" +
" \"store\": {\n" +
" \"book\": [\n" +
" {\n" +
" \"price\": \"120\"\n" +
" },\n" +
" {\n" +
" \"price\": 8.95\n" +
" },\n" +
" {\n" +
" \"price\": 12.99\n" +
" },\n" +
" {\n" +
" \"price\": 8.99\n" +
" },\n" +
" {\n" +
" \"price\": 22.99\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"expensive\": 10\n" +
"}";
List<Double> numbers = JsonPath.read(json, "$.store.book[?(@.price <= 90)].price");
assertThat(numbers).containsExactly(8.95D, 12.99D, 8.99D, 22.99D);
}
@Test(expected = PathNotFoundException.class)
public void github_89() {
com.google.gson.JsonObject json = new JsonObject();
json.addProperty("foo", "bar");
JsonPath path = JsonPath.compile("$.foo");
String object = path.read(json);
}
}

Loading…
Cancel
Save