diff --git a/json-path-assert/src/test/resources/lotto.json b/json-path-assert/src/test/resources/lotto.json index 853e62d9..eb4e4691 100644 --- a/json-path-assert/src/test/resources/lotto.json +++ b/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]}]}} \ No newline at end of file +{ + "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 + ] + } + ] + } +} \ No newline at end of file diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java index 67a95f16..68572b1a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/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"); } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java index d59c171d..99d2bb61 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java +++ b/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()) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java index 720728a2..bfd31564 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java +++ b/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();