Skip to content

Commit 9729cb0

Browse files
authored
Defines the pattern for taking item from collection of JSONArray by index (#842)
1 parent 2eed3b9 commit 9729cb0

File tree

8 files changed

+178
-14
lines changed

8 files changed

+178
-14
lines changed

README.md

+14-12
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,20 @@ Functions
8383
Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression.
8484
The function output is dictated by the function itself.
8585

86-
| Function | Description | Output type |
87-
| :------------------------ | :------------------------------------------------------------------ |:----------- |
88-
| min() | Provides the min value of an array of numbers | Double |
89-
| max() | Provides the max value of an array of numbers | Double |
90-
| avg() | Provides the average value of an array of numbers | Double |
91-
| stddev() | Provides the standard deviation value of an array of numbers | Double |
92-
| length() | Provides the length of an array | Integer |
93-
| sum() | Provides the sum value of an array of numbers | Double |
94-
| keys() | Provides the property keys (An alternative for terminal tilde `~`) | `Set<E>` |
95-
| concat(X) | Provides a concatinated version of the path output with a new item | like input |
96-
| append(X) | add an item to the json path output array | like input |
97-
86+
| Function | Description | Output type |
87+
|:----------|:-------------------------------------------------------------------------------------|:---------------------|
88+
| min() | Provides the min value of an array of numbers | Double |
89+
| max() | Provides the max value of an array of numbers | Double |
90+
| avg() | Provides the average value of an array of numbers | Double |
91+
| stddev() | Provides the standard deviation value of an array of numbers | Double |
92+
| length() | Provides the length of an array | Integer |
93+
| sum() | Provides the sum value of an array of numbers | Double |
94+
| keys() | Provides the property keys (An alternative for terminal tilde `~`) | `Set<E>` |
95+
| concat(X) | Provides a concatinated version of the path output with a new item | like input |
96+
| append(X) | add an item to the json path output array | like input |
97+
| first() | Provides the first item of an array | Depends on the array |
98+
| last() | Provides the last item of an array | Depends on the array |
99+
| index(X) | Provides the item of an array of index: X, if the X is negative, take from backwards | Depends on the array |
98100
Filter Operators
99101
-----------------
100102

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

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import com.jayway.jsonpath.internal.function.numeric.Min;
99
import com.jayway.jsonpath.internal.function.numeric.StandardDeviation;
1010
import com.jayway.jsonpath.internal.function.numeric.Sum;
11+
import com.jayway.jsonpath.internal.function.sequence.First;
12+
import com.jayway.jsonpath.internal.function.sequence.Index;
13+
import com.jayway.jsonpath.internal.function.sequence.Last;
1114
import com.jayway.jsonpath.internal.function.text.Concatenate;
1215
import com.jayway.jsonpath.internal.function.text.Length;
1316

@@ -46,6 +49,11 @@ public class PathFunctionFactory {
4649
map.put("size", Length.class);
4750
map.put("append", Append.class);
4851
map.put("keys", KeySetFunction.class);
52+
53+
// Sequential Functions
54+
map.put("first", First.class);
55+
map.put("last", Last.class);
56+
map.put("index", Index.class);
4957

5058

5159
FUNCTIONS = Collections.unmodifiableMap(map);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.jayway.jsonpath.internal.function.sequence;
2+
3+
import com.jayway.jsonpath.JsonPathException;
4+
import com.jayway.jsonpath.internal.EvaluationContext;
5+
import com.jayway.jsonpath.internal.PathRef;
6+
import com.jayway.jsonpath.internal.function.Parameter;
7+
import com.jayway.jsonpath.internal.function.PathFunction;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
/**
13+
* Defines the pattern for taking item from collection of JSONArray by index
14+
*
15+
* Created by git9527 on 6/11/22.
16+
*/
17+
public abstract class AbstractSequenceAggregation implements PathFunction {
18+
19+
protected abstract int targetIndex(EvaluationContext ctx, List<Parameter> parameters);
20+
21+
@Override
22+
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List<Parameter> parameters) {
23+
if(ctx.configuration().jsonProvider().isArray(model)){
24+
25+
Iterable<?> objects = ctx.configuration().jsonProvider().toIterable(model);
26+
List<Object> objectList = new ArrayList<>();
27+
objects.forEach(objectList::add);
28+
int targetIndex = this.targetIndex(ctx, parameters);
29+
if (targetIndex >= 0) {
30+
return objectList.get(targetIndex);
31+
} else {
32+
int realIndex = objectList.size() + targetIndex;
33+
if (realIndex > 0) {
34+
return objectList.get(realIndex);
35+
} else {
36+
throw new JsonPathException("Target index:" + targetIndex + " larger than object count:" + objectList.size());
37+
}
38+
}
39+
}
40+
throw new JsonPathException("Aggregation function attempted to calculate value using empty array");
41+
}
42+
43+
protected int getIndexFromParameters(EvaluationContext ctx, List<Parameter> parameters) {
44+
List<Number> numbers = Parameter.toList(Number.class, ctx, parameters);
45+
return numbers.get(0).intValue();
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.jayway.jsonpath.internal.function.sequence;
2+
3+
import com.jayway.jsonpath.internal.EvaluationContext;
4+
import com.jayway.jsonpath.internal.function.Parameter;
5+
6+
import java.util.List;
7+
8+
/**
9+
* Take the first item from collection of JSONArray
10+
*
11+
* Created by git9527 on 6/11/22.
12+
*/
13+
public class First extends AbstractSequenceAggregation {
14+
@Override
15+
protected int targetIndex(EvaluationContext ctx, List<Parameter> parameters) {
16+
return 0;
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.jayway.jsonpath.internal.function.sequence;
2+
3+
import com.jayway.jsonpath.internal.EvaluationContext;
4+
import com.jayway.jsonpath.internal.function.Parameter;
5+
6+
import java.util.List;
7+
8+
/**
9+
* Take the index from first Parameter, then the item from collection of JSONArray by index
10+
*
11+
* Created by git9527 on 6/11/22.
12+
*/
13+
public class Index extends AbstractSequenceAggregation {
14+
@Override
15+
protected int targetIndex(EvaluationContext ctx, List<Parameter> parameters) {
16+
return getIndexFromParameters(ctx, parameters);
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.jayway.jsonpath.internal.function.sequence;
2+
3+
import com.jayway.jsonpath.internal.EvaluationContext;
4+
import com.jayway.jsonpath.internal.function.Parameter;
5+
6+
import java.util.List;
7+
8+
/**
9+
* Take the first item from collection of JSONArray
10+
*
11+
* Created by git9527 on 6/11/22.
12+
*/
13+
public class Last extends AbstractSequenceAggregation {
14+
@Override
15+
protected int targetIndex(EvaluationContext ctx, List<Parameter> parameters) {
16+
return -1;
17+
}
18+
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ private List<Parameter> parseFunctionParameters(String funcName) {
305305
continue;
306306
}
307307

308-
if (c == OPEN_BRACE || isDigit(c) || DOUBLE_QUOTE == c) {
308+
if (c == OPEN_BRACE || isDigit(c) || DOUBLE_QUOTE == c || MINUS == c) {
309309
type = ParamType.JSON;
310310
}
311311
else if (isPathContext(c)) {
@@ -612,7 +612,7 @@ private boolean readBracketPropertyToken(PathTokenAppender appender) {
612612
inProperty = true;
613613
lastSignificantWasComma = false;
614614
}
615-
} else if (c == COMMA && !inProperty) {
615+
} else if (c == COMMA && !inProperty) {
616616
if (lastSignificantWasComma){
617617
fail("Found empty property at index "+readPosition);
618618
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.jayway.jsonpath.internal.function;
2+
3+
import com.jayway.jsonpath.Configuration;
4+
import com.jayway.jsonpath.Configurations;
5+
import org.junit.Test;
6+
7+
/**
8+
* Test cases for functions
9+
*
10+
* -first
11+
* -last
12+
* -index(X)
13+
*
14+
* Created by git9527 on 6/11/22.
15+
*/
16+
public class SequentialPathFunctionTest extends BaseFunctionTest {
17+
18+
private Configuration conf = Configurations.JACKSON_CONFIGURATION;
19+
20+
@Test
21+
public void testFirstOfNumbers() throws Exception {
22+
verifyFunction(conf, "$.numbers.first()", NUMBER_SERIES, 1);
23+
}
24+
25+
@Test
26+
public void testLastOfNumbers() throws Exception {
27+
verifyFunction(conf, "$.numbers.last()", NUMBER_SERIES, 10);
28+
}
29+
30+
@Test
31+
public void testIndexOfNumbers() throws Exception {
32+
verifyFunction(conf, "$.numbers.index(0)", NUMBER_SERIES, 1);
33+
verifyFunction(conf, "$.numbers.index(-1)", NUMBER_SERIES, 10);
34+
verifyFunction(conf, "$.numbers.index(1)", NUMBER_SERIES, 2);
35+
}
36+
37+
@Test
38+
public void testFirstOfText() throws Exception {
39+
verifyFunction(conf, "$.text.first()", TEXT_SERIES, "a");
40+
}
41+
42+
@Test
43+
public void testLastOfText() throws Exception {
44+
verifyFunction(conf, "$.text.last()", TEXT_SERIES, "f");
45+
}
46+
47+
@Test
48+
public void testIndexOfText() throws Exception {
49+
verifyFunction(conf, "$.text.index(0)", TEXT_SERIES, "a");
50+
verifyFunction(conf, "$.text.index(-1)", TEXT_SERIES, "f");
51+
verifyFunction(conf, "$.text.index(1)", TEXT_SERIES, "b");
52+
}
53+
}

0 commit comments

Comments
 (0)