Browse Source

initial commit of function support providing math / JSON helper routines in path execution

pull/103/head
Matt Greenwood 9 years ago
parent
commit
89e71a9353
  1. 40
      json-path-assert/src/test/resources/lotto.json
  2. 33
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  3. 44
      json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java
  4. 20
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java

40
json-path-assert/src/test/resources/lotto.json

@ -1 +1,39 @@
{"lotto":{"lottoId":5,"winning-numbers":[2,45,34,23,7,5,3],"winners":[{"winnerId":23,"numbers":[2,45,34,23,3,5]},{"winnerId":54,"numbers":[52,3,12,11,18,22]}]}}
{
"lotto": {
"maxAverage": 100,
"lottoId": 5,
"winning-numbers": [
2,
45,
34,
23,
7,
5,
3
],
"winners": [
{
"winnerId": 23,
"numbers": [
2,
45,
34,
23,
3,
5
]
},
{
"winnerId": 54,
"numbers": [
52,
3,
12,
11,
18,
22
]
}
]
}
}

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

@ -39,7 +39,7 @@ public class Criteria implements Predicate {
private static final Logger logger = LoggerFactory.getLogger(Criteria.class);
private static final String[] OPERATORS = {
public static final String[] OPERATORS = {
CriteriaType.EQ.toString(),
CriteriaType.GTE.toString(),
CriteriaType.LTE.toString(),
@ -314,24 +314,23 @@ public class Criteria implements Predicate {
abstract boolean eval(Object expected, Object model, PredicateContext ctx);
/**
* Retrieve the CriteriaType based on the incoming string token - assumes the CriteriaType toString()
* method will match the string token presented
*
* @param str
* The string token representing the CriteriaType
*
* @return
* The enum type CriteriaType
*/
public static CriteriaType parse(String str) {
if ("==".equals(str)) {
return EQ;
} else if (">".equals(str)) {
return GT;
} else if (">=".equals(str)) {
return GTE;
} else if ("<".equals(str)) {
return LT;
} else if ("<=".equals(str)) {
return LTE;
} else if ("!=".equals(str)) {
return NE;
} else if ("=~".equals(str)) {
return REGEX;
} else {
throw new UnsupportedOperationException("CriteriaType " + str + " can not be parsed");
for (CriteriaType value : values()) {
if (value.toString().matches(str)) {
return value;
}
}
throw new UnsupportedOperationException("CriteriaType " + str + " can not be parsed");
}
}

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

@ -17,13 +17,7 @@ package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.token.ArrayPathToken;
import com.jayway.jsonpath.internal.token.PathToken;
import com.jayway.jsonpath.internal.token.PredicatePathToken;
import com.jayway.jsonpath.internal.token.PropertyPathToken;
import com.jayway.jsonpath.internal.token.RootPathToken;
import com.jayway.jsonpath.internal.token.ScanPathToken;
import com.jayway.jsonpath.internal.token.WildcardPathToken;
import com.jayway.jsonpath.internal.token.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -47,6 +41,9 @@ public class PathCompiler {
private static final char BRACKET_OPEN = '[';
private static final char BRACKET_CLOSE = ']';
private static final char SPACE = ' ';
private static final char PERCENT = '%';
private static final char PAREN_OPEN = '(';
private static final char PAREN_CLOSE = ')';
private static final Cache cache = new Cache(200);
@ -107,6 +104,11 @@ public class PathCompiler {
fragment = path.substring(i, i + positions);
i += positions;
break;
case PERCENT:
positions = fastForwardUntilCloseParens(path, i);
fragment = path.substring(i, i + positions);
i += positions;
break;
case PERIOD:
i++;
if (path.charAt(i) == PERIOD) {
@ -185,6 +187,33 @@ public class PathCompiler {
return skipCount;
}
private static int fastForwardUntilCloseParens(String s, int index) {
int skipCount = 0;
int nestedParens = 0;
//First char is always '[' no need to check it
index++;
skipCount++;
while (index < s.length()) {
char current = s.charAt(index);
index++;
skipCount++;
if (current == PAREN_CLOSE && nestedParens == 0) {
break;
}
if (current == PAREN_OPEN) {
nestedParens++;
}
if (current == PAREN_CLOSE) {
nestedParens--;
}
}
return skipCount;
}
private static int fastForwardUntilClosed(String s, int index) {
int skipCount = 0;
int nestedBrackets = 0;
@ -242,6 +271,7 @@ public class PathCompiler {
else if ("..".equals(pathFragment)) return new ScanPathToken();
else if ("[*]".equals(pathFragment)) return new WildcardPathToken();
else if (".*".equals(pathFragment)) return new WildcardPathToken();
else if (".%".equals(pathFragment) || pathFragment.startsWith("['%")) return new FunctionPathToken(pathFragment);
else if ("[?]".equals(pathFragment)) return new PredicatePathToken(filterList.poll());
else if (FILTER_PATTERN.matcher(pathFragment).matches()) {

20
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;
@ -67,7 +73,11 @@ public abstract class PathToken {
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP;
if (isLeaf()) {
ctx.addResult(evalPath, pathRef, propertyVal);
} else {
// } else if (isFunction()) {
// Function function = FunctionFactory.newFunction(next.getPathFragment());
// next.invoke(function, evalPath, pathRef, propertyVal, ctx);
}
else {
next().evaluate(evalPath, pathRef, propertyVal, ctx);
}
} else {
@ -143,6 +153,10 @@ public abstract class PathToken {
return next == null;
}
// boolean isFunction() {
// return null != next && next.getPathFragment().startsWith("['%");
// }
boolean isRoot() {
return prev == null;
}
@ -201,6 +215,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);
abstract boolean isTokenDefinite();

Loading…
Cancel
Save