Browse Source

fix issue 654 by adding a new option FILTER_AS_ARRAY.

pull/834/head
wls2002 3 years ago
parent
commit
98124ffda2
  1. 43
      json-path/src/main/java/com/jayway/jsonpath/Option.java
  2. 14
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java
  3. 14
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java
  4. 38
      json-path/src/test/java/com/jayway/jsonpath/Issue_654.java

43
json-path/src/main/java/com/jayway/jsonpath/Option.java

@ -83,6 +83,47 @@ public enum Option {
* If REQUIRE_PROPERTIES option is present PathNotFoundException is thrown. * If REQUIRE_PROPERTIES option is present PathNotFoundException is thrown.
* If REQUIRE_PROPERTIES option is not present ["b-val"] is returned. * If REQUIRE_PROPERTIES option is not present ["b-val"] is returned.
*/ */
REQUIRE_PROPERTIES REQUIRE_PROPERTIES,
/**
* This mode can be used query at special filters.
* In the origin design, the filter receive an array and return an array.
* However, the subsequent operation will apply on each element in the returned array, rather than the whole array itself.
* The mode treats the result of filter as array, and it might be useful to some situation.
* For example:
*
* Given:
* <pre>
* [
* {
* "price" : 20,
* "name" : "book1"
* },
* {
* "price" : 30,
* "name" : "book1"
* },
* {
* "price" : 40,
* "name" : "book1"
* },
* {
* "price" : 50,
* "name" : "book1"
* },
* ]
* </pre>
*
* We want to get the first book which price is greater than 25.
*
* evaluating the path "$[?(@.price > 25)][0]"
*
* when using classic mode, the result will be null.
* when using this mode, the result will be [{"price":30,"name":"book1"}]. That is what we expected.
*
*
* Notice: When using this mode, the path of the result will be incorrect. Besides, SET operation will don't work.
*/
FILTER_AS_ARRAY
} }

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

@ -146,6 +146,20 @@ public abstract class PathToken {
} }
} }
protected void handleWholeArray(String currentPath, Object model, EvaluationContextImpl ctx){
// using FILTER_AS_ARRAY mode, details at com/jayway/jsonpath/Option.FILTER_AS_ARRAY
// NOTICE: When using this mode, the path of the result will be incorrect. Besides, SET operation will don't work.
if(isLeaf()){
Iterable<?> it = ctx.jsonProvider().toIterable(model);
for(Object object : it){
ctx.addResult(currentPath, PathRef.NO_OP, object); // Use PathRef.NO_OP because the SET operation is banned.
}
}
else{
next().evaluate(currentPath, PathRef.NO_OP, model, ctx);
}
}
PathToken prev(){ PathToken prev(){
return prev; return prev;
} }

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

@ -16,11 +16,13 @@ package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.InvalidPathException; import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.Predicate; import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.PathRef; import com.jayway.jsonpath.internal.PathRef;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import static java.lang.String.format; import static java.lang.String.format;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@ -53,6 +55,17 @@ public class PredicatePathToken extends PathToken {
} }
} }
} else if (ctx.jsonProvider().isArray(model)){ } else if (ctx.jsonProvider().isArray(model)){
if (ctx.configuration().containsOption(Option.FILTER_AS_ARRAY)){
//using FILTER_AS_ARRAY mode, details at com/jayway/jsonpath/Option.FILTER_AS_ARRAY
Iterable<?> objects = ctx.jsonProvider().toIterable(model);
Object filteredModel = ctx.jsonProvider().createArray();
for(Object idxModel : objects){
if(accept(idxModel, ctx.rootDocument(), ctx.configuration(), ctx)){
((List)filteredModel).add(idxModel);
}
}
handleWholeArray(currentPath, filteredModel, ctx);
} else {
int idx = 0; int idx = 0;
Iterable<?> objects = ctx.jsonProvider().toIterable(model); Iterable<?> objects = ctx.jsonProvider().toIterable(model);
@ -62,6 +75,7 @@ public class PredicatePathToken extends PathToken {
} }
idx++; idx++;
} }
}
} else { } else {
if (isUpstreamDefinite()) { if (isUpstreamDefinite()) {
throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model)); throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model));

38
json-path/src/test/java/com/jayway/jsonpath/Issue_654.java

@ -0,0 +1,38 @@
package com.jayway.jsonpath;
import org.junit.Test;
public class Issue_654 {
public static final Configuration conf = Configuration.builder().options(Option.FILTER_AS_ARRAY).build();
public static final String json = "[\n" +
" [0, 1, 2], \n" +
" [3, 4, 5],\n" +
" [6, 7, 8],\n" +
" [9, 10, 11],\n" +
" [12, 13, 14]\n" +
"]";
@Test
public void test_1(){
// find the first array that its first element is greater than 4
Object res_ori = JsonPath.parse(json).read("$[?(@[0] > 4)][0]");
Object res_new = JsonPath.using(conf).parse(json).read("$[?(@[0] > 4)][0]");
assert (res_ori.toString().equals("[6,9,12]")); // origin mode will use INDEX_AT(0) operation on [6, 7, 8], [9, 10, 11], [12, 13, 14] respectively;
assert (res_new.toString().equals("[[6,7,8]]")); // new mode use INDEX_AT(0) operation on ([6, 7, 8], [9, 10, 11], [12, 13, 14])
}
@Test
public void test_2(){
// find the count of the elements whose first element is greater than 4
Object res_ori = JsonPath.parse(json).read("$[?(@[0] > 4)].length()");
Object res_new = JsonPath.using(conf).parse(json).read("$[?(@[0] > 4)].length()");
assert (res_ori.toString().equals("[3,3,3]")); // origin mode will use length() function on [6, 7, 8], [9, 10, 11], [12, 13, 14] respectively;
assert (res_new.toString().equals("[3]")); // new mode use length() function on ([6, 7, 8], [9, 10, 11], [12, 13, 14])
}
}
Loading…
Cancel
Save