Skip to content

Commit 6a40215

Browse files
Support for running functions over a result set.
1 parent d958ab7 commit 6a40215

File tree

5 files changed

+85
-1
lines changed

5 files changed

+85
-1
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,25 @@ public EvaluationContext evaluate(Object document, Object rootDocument, Configur
5050
EvaluationContextImpl ctx = new EvaluationContextImpl(this, rootDocument, configuration, forUpdate);
5151
try {
5252
PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP;
53-
root.evaluate("", op, document, ctx);
53+
54+
if (root.isFunctionPath()) {
55+
// Remove the functionPath and evaluate the resulting path.
56+
PathToken funcToken = root.chop();
57+
root.evaluate("", op, document, ctx);
58+
// Get the value of the evaluation to use as model when evaluating the function.
59+
Object arrayModel = ctx.getValue(false);
60+
61+
// Evaluate the function on the model from the first evaluation.
62+
RootPathToken newRoot = new RootPathToken('x');
63+
newRoot.append(funcToken);
64+
CompiledPath newCPath = new CompiledPath(newRoot, true);
65+
EvaluationContextImpl newCtx = new EvaluationContextImpl(newCPath, arrayModel, configuration, false);
66+
funcToken.evaluate("", op, arrayModel, newCtx);
67+
return newCtx;
68+
} else {
69+
root.evaluate("", op, document, ctx);
70+
return ctx;
71+
}
5472
} catch (EvaluationAbortException abort){};
5573

5674
return ctx;

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ PathToken appendTailToken(PathToken next) {
3636
return next;
3737
}
3838

39+
PathToken remove() {
40+
prev.next = next;
41+
if (next == null) {
42+
return prev;
43+
}
44+
next.prev = prev;
45+
return next;
46+
}
47+
3948
void handleObjectProperty(String currentPath, Object model, EvaluationContextImpl ctx, List<String> properties) {
4049

4150
if(properties.size() == 1) {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public RootPathToken append(PathToken next) {
4343
return this;
4444
}
4545

46+
public PathToken chop() {
47+
// Remove tail.
48+
PathToken oldTail = tail;
49+
tail = tail.remove();
50+
return oldTail;
51+
}
52+
4653
public PathTokenAppender getPathTokenAppender(){
4754
return new PathTokenAppender(){
4855
@Override

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
public class BaseFunctionTest {
1515
protected static final String NUMBER_SERIES = "{\"empty\": [], \"numbers\" : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}";
1616
protected static final String TEXT_SERIES = "{\"urls\": [\"http://api.worldbank.org/countries/all/?format=json\", \"http://api.worldbank.org/countries/all/?format=json\"], \"text\" : [ \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" ]}";
17+
protected static final String EXAMPLE_SERIES = "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";
1718

1819
/**
1920
* Verify the function returns the correct result based on the input expectedValue
@@ -40,6 +41,10 @@ protected void verifyTextFunction(Configuration conf, String pathExpr, Object ex
4041
verifyFunction(conf, pathExpr, TEXT_SERIES, expectedValue);
4142
}
4243

44+
protected void verifyExampleFunction(Configuration conf, String pathExpr, Object expectedValue) {
45+
verifyFunction(conf, pathExpr, EXAMPLE_SERIES, expectedValue);
46+
}
47+
4348
protected String getResourceAsText(String resourceName) throws IOException {
4449
return new Scanner(BaseFunctionTest.class.getResourceAsStream(resourceName), "UTF-8").useDelimiter("\\A").next();
4550
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.jayway.jsonpath.internal.function;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.runners.Parameterized.Parameters;
5+
6+
import com.jayway.jsonpath.Configuration;
7+
import com.jayway.jsonpath.Configurations;
8+
import com.jayway.jsonpath.JsonPathException;
9+
import org.junit.Test;
10+
import org.junit.runner.RunWith;
11+
import org.junit.runners.Parameterized;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
/**
16+
* Defines functional tests around executing functions on result sets.
17+
*/
18+
@RunWith(Parameterized.class)
19+
public class ResultSetFunctionTest extends BaseFunctionTest {
20+
21+
private static final Logger logger = LoggerFactory.getLogger(ResultSetFunctionTest.class);
22+
23+
private Configuration conf = Configurations.GSON_CONFIGURATION;
24+
25+
public ResultSetFunctionTest(Configuration conf) {
26+
logger.debug("Testing with configuration {}", conf.getClass().getName());
27+
this.conf = conf;
28+
}
29+
30+
@Parameters
31+
public static Iterable<Configuration> configurations() {
32+
return Configurations.configurations();
33+
}
34+
35+
@Test
36+
public void testMaxOfDoublesResultSet() {
37+
verifyExampleFunction(conf, "$.store.book[*].price.max()", 22.99);
38+
}
39+
40+
@Test
41+
public void testSumOfDoublesResultSet() {
42+
verifyExampleFunction(conf, "$.store.book[*].price.sum()", 53.92);
43+
}
44+
45+
}

0 commit comments

Comments
 (0)