Browse Source

Inline criteria handles path stmt on both sides of operator.

pull/59/head
Kalle Stenflo 10 years ago
parent
commit
ca4d18911e
  1. 2
      json-path-web-test/src/main/resources/webapp/index.html
  2. 371
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  3. 34
      json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java
  4. 6
      json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java

2
json-path-web-test/src/main/resources/webapp/index.html

@ -147,7 +147,7 @@
<div class="tab-pane active" id="jayway-tab"> <div class="tab-pane active" id="jayway-tab">
<br/> <br/>
<a href="https://code.google.com/p/json-path/" target="_blank" style="float: right">About implementation...</a> <a href="https://code.google.com/p/json-left/" target="_blank" style="float: right">About implementation...</a>
<span id="jayway-time"></span>&nbsp;millis <span id="jayway-time"></span>&nbsp;millis
<hr/> <hr/>
<div class="row"> <div class="row">

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

@ -48,18 +48,18 @@ public class Criteria implements Predicate {
CriteriaType.GT.toString() CriteriaType.GT.toString()
}; };
private final Path path; private Object left;
private CriteriaType criteriaType; private CriteriaType criteriaType;
private Object expected; private Object right;
private final List<Criteria> criteriaChain; private final List<Criteria> criteriaChain;
private static enum CriteriaType { private static enum CriteriaType {
EQ { EQ {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
boolean res = (0 == safeCompare(expected, actual)); boolean res = (0 == safeCompare(left, right));
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left, res);
return res; return res;
} }
@ -70,9 +70,9 @@ public class Criteria implements Predicate {
}, },
NE { NE {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
boolean res = (0 != safeCompare(expected, actual)); boolean res = (0 != safeCompare(left, right));
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left, res);
return res; return res;
} }
@ -83,12 +83,12 @@ public class Criteria implements Predicate {
}, },
GT { GT {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
if ((expected == null) ^ (actual == null)) { if ((left == null) ^ (right == null)) {
return false; return false;
} }
boolean res = (0 > safeCompare(expected, actual)); boolean res = (0 > safeCompare(left, right));
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left, res);
return res; return res;
} }
@Override @Override
@ -98,12 +98,12 @@ public class Criteria implements Predicate {
}, },
GTE { GTE {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
if ((expected == null) ^ (actual == null)) { if ((left == null) ^ (right == null)) {
return false; return false;
} }
boolean res = (0 >= safeCompare(expected, actual)); boolean res = (0 >= safeCompare(left, right));
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left, res);
return res; return res;
} }
@Override @Override
@ -113,12 +113,12 @@ public class Criteria implements Predicate {
}, },
LT { LT {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
if ((expected == null) ^ (actual == null)) { if ((left == null) ^ (right == null)) {
return false; return false;
} }
boolean res = (0 < safeCompare(expected, actual)); boolean res = (0 < safeCompare(left, right));
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left, res);
return res; return res;
} }
@Override @Override
@ -128,12 +128,12 @@ public class Criteria implements Predicate {
}, },
LTE { LTE {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
if ((expected == null) ^ (actual == null)) { if ((left == null) ^ (right == null)) {
return false; return false;
} }
boolean res = (0 <= safeCompare(expected, actual)); boolean res = (0 <= safeCompare(left, right));
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left, res);
return res; return res;
} }
@Override @Override
@ -143,37 +143,37 @@ public class Criteria implements Predicate {
}, },
IN { IN {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
boolean res = false; boolean res = false;
Collection exps = (Collection) expected; Collection exps = (Collection) left;
for (Object exp : exps) { for (Object exp : exps) {
if (0 == safeCompare(exp, actual)) { if (0 == safeCompare(exp, right)) {
res = true; res = true;
break; break;
} }
} }
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", exps), res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), join(", ", exps), res);
return res; return res;
} }
}, },
NIN { NIN {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
Collection nexps = (Collection) expected; Collection nexps = (Collection) left;
boolean res = !nexps.contains(actual); boolean res = !nexps.contains(right);
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", nexps), res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), join(", ", nexps), res);
return res; return res;
} }
}, },
ALL { ALL {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
boolean res = true; boolean res = true;
Collection exps = (Collection) expected; Collection exps = (Collection) left;
if (ctx.configuration().jsonProvider().isArray(actual)) { if (ctx.configuration().jsonProvider().isArray(right)) {
for (Object exp : exps) { for (Object exp : exps) {
boolean found = false; boolean found = false;
for (Object check : ctx.configuration().jsonProvider().toIterable(actual)) { for (Object check : ctx.configuration().jsonProvider().toIterable(right)) {
if (0 == safeCompare(exp, check)) { if (0 == safeCompare(exp, check)) {
found = true; found = true;
break; break;
@ -184,7 +184,7 @@ public class Criteria implements Predicate {
break; break;
} }
} }
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", join(", ", ctx.configuration().jsonProvider().toIterable(actual)), name(), join(", ", exps), res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", join(", ", ctx.configuration().jsonProvider().toIterable(right)), name(), join(", ", exps), res);
} else { } else {
res = false; res = false;
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", "<NOT AN ARRAY>", name(), join(", ", exps), res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", "<NOT AN ARRAY>", name(), join(", ", exps), res);
@ -194,71 +194,71 @@ public class Criteria implements Predicate {
}, },
SIZE { SIZE {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
int size = (Integer) expected; int size = (Integer) left;
boolean res; boolean res;
if (ctx.configuration().jsonProvider().isArray(actual)) { if (ctx.configuration().jsonProvider().isArray(right)) {
int length = ctx.configuration().jsonProvider().length(actual); int length = ctx.configuration().jsonProvider().length(right);
res = (length == size); res = (length == size);
if(logger.isDebugEnabled()) logger.debug("Array with size {} {} {} => {}", length, name(), size, res); if(logger.isDebugEnabled()) logger.debug("Array with size {} {} {} => {}", length, name(), size, res);
} else if (actual instanceof String) { } else if (right instanceof String) {
int length = ((String) actual).length(); int length = ((String) right).length();
res = length == size; res = length == size;
if(logger.isDebugEnabled()) logger.debug("String with length {} {} {} => {}", length, name(), size, res); if(logger.isDebugEnabled()) logger.debug("String with length {} {} {} => {}", length, name(), size, res);
} else { } else {
res = false; res = false;
if(logger.isDebugEnabled()) logger.debug("{} {} {} => {}", actual == null ? "null" : actual.getClass().getName(), name(), size, res); if(logger.isDebugEnabled()) logger.debug("{} {} {} => {}", right == null ? "null" : right.getClass().getName(), name(), size, res);
} }
return res; return res;
} }
}, },
EXISTS { EXISTS {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
//This must be handled outside //This must be handled outside
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
}, },
TYPE { TYPE {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
final Class<?> expType = (Class<?>) expected; final Class<?> expType = (Class<?>) left;
final Class<?> actType = actual == null ? null : actual.getClass(); final Class<?> actType = right == null ? null : right.getClass();
return actType != null && expType.isAssignableFrom(actType); return actType != null && expType.isAssignableFrom(actType);
} }
}, },
REGEX { REGEX {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
boolean res = false; boolean res = false;
final Pattern pattern = (Pattern) expected; final Pattern pattern = (Pattern) left;
if (actual != null && actual instanceof String) { if (right != null && right instanceof String) {
res = pattern.matcher(actual.toString()).matches(); res = pattern.matcher(right.toString()).matches();
} }
if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", actual, name(), expected.toString(), res); if(logger.isDebugEnabled()) logger.debug("[{}] {} [{}] => {}", right, name(), left.toString(), res);
return res; return res;
} }
}, },
MATCHES { MATCHES {
@Override @Override
boolean eval(Object expected, final Object actual, final PredicateContext ctx) { boolean eval(Object left, final Object right, final PredicateContext ctx) {
PredicateContextImpl pci = (PredicateContextImpl) ctx; PredicateContextImpl pci = (PredicateContextImpl) ctx;
Predicate exp = (Predicate) expected; Predicate exp = (Predicate) left;
return exp.apply(new PredicateContextImpl(actual, ctx.root(), ctx.configuration(), pci.documentPathCache())); return exp.apply(new PredicateContextImpl(right, ctx.root(), ctx.configuration(), pci.documentPathCache()));
} }
}, },
NOT_EMPTY { NOT_EMPTY {
@Override @Override
boolean eval(Object expected, Object actual, PredicateContext ctx) { boolean eval(Object left, Object right, PredicateContext ctx) {
boolean res = false; boolean res = false;
if (actual != null) { if (right != null) {
if (ctx.configuration().jsonProvider().isArray(actual)) { if (ctx.configuration().jsonProvider().isArray(right)) {
int len = ctx.configuration().jsonProvider().length(actual); int len = ctx.configuration().jsonProvider().length(right);
res = (0 != len); res = (0 != len);
if(logger.isDebugEnabled()) logger.debug("array length = {} {} => {}", len, name(), res); if(logger.isDebugEnabled()) logger.debug("array length = {} {} => {}", len, name(), res);
} else if (actual instanceof String) { } else if (right instanceof String) {
int len = ((String) actual).length(); int len = ((String) right).length();
res = (0 != len); res = (0 != len);
if(logger.isDebugEnabled()) logger.debug("string length = {} {} => {}", len, name(), res); if(logger.isDebugEnabled()) logger.debug("string length = {} {} => {}", len, name(), res);
} }
@ -267,7 +267,7 @@ public class Criteria implements Predicate {
} }
}; };
abstract boolean eval(Object expected, Object actual, PredicateContext ctx); abstract boolean eval(Object left, Object right, PredicateContext ctx);
public static CriteriaType parse(String str) { public static CriteriaType parse(String str) {
if ("==".equals(str)) { if ("==".equals(str)) {
@ -288,23 +288,26 @@ public class Criteria implements Predicate {
} }
} }
private Criteria(List<Criteria> criteriaChain, Path path) { private Criteria(List<Criteria> criteriaChain, Object left) {
if (!path.isDefinite()) {
throw new InvalidCriteriaException("A criteria path must be definite. The path " + path.toString() + " is not!"); if(left instanceof Path) {
if (!((Path)left).isDefinite()) {
throw new InvalidCriteriaException("A criteria path must be definite. The path " + left.toString() + " is not!");
}
} }
this.path = path; this.left = left;
this.criteriaChain = criteriaChain; this.criteriaChain = criteriaChain;
this.criteriaChain.add(this); this.criteriaChain.add(this);
} }
private Criteria(Path path) { private Criteria(Object left) {
this(new LinkedList<Criteria>(), path); this(new LinkedList<Criteria>(), left);
} }
private Criteria(Path path, CriteriaType criteriaType, Object expected) { private Criteria(Object left, CriteriaType criteriaType, Object right) {
this(new LinkedList<Criteria>(), path); this(new LinkedList<Criteria>(), left);
this.criteriaType = criteriaType; this.criteriaType = criteriaType;
this.expected = expected; this.right = right;
} }
@ -318,35 +321,39 @@ public class Criteria implements Predicate {
return true; return true;
} }
private Object evaluateIfPath(Object target, PredicateContext ctx){
Object res = target;
if(res instanceof Path){
Path leftPath = (Path) target;
if(ctx instanceof PredicateContextImpl){
//This will use cache for document ($) queries
PredicateContextImpl ctxi = (PredicateContextImpl) ctx;
res = ctxi.evaluate(leftPath);
} else {
Object doc = leftPath.isRootPath()?ctx.root():ctx.item();
res = leftPath.evaluate(doc, ctx.root(), ctx.configuration()).getValue();
}
}
return res;
}
private boolean eval(PredicateContext ctx) { private boolean eval(PredicateContext ctx) {
if (CriteriaType.EXISTS == criteriaType) { if (CriteriaType.EXISTS == criteriaType) {
boolean exists = ((Boolean) expected); boolean exists = ((Boolean) right);
try { try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build(); Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
path.evaluate(ctx.item(), ctx.root(), c).getValue(); ((Path)left).evaluate(ctx.item(), ctx.root(), c).getValue();
return exists; return exists;
} catch (PathNotFoundException e) { } catch (PathNotFoundException e) {
return !exists; return !exists;
} }
} else { } else {
try { try {
final Object actual = path.evaluate(ctx.item(), ctx.root(), ctx.configuration()).getValue(); Object leftVal = evaluateIfPath(left, ctx);
Object rightVal = evaluateIfPath(right, ctx);
Object expectedVal = expected;
if(expected instanceof Path){
Path expectedPath = (Path) expected;
if(ctx instanceof PredicateContextImpl){
//This will use cache for document ($) queries
PredicateContextImpl ctxi = (PredicateContextImpl) ctx;
expectedVal = ctxi.evaluate(expectedPath);
} else {
Object doc = expectedPath.isRootPath()?ctx.root():ctx.item();
expectedVal = expectedPath.evaluate(doc, ctx.root(), ctx.configuration()).getValue();
}
}
return criteriaType.eval(expectedVal, actual, ctx); return criteriaType.eval(rightVal, leftVal, ctx);
} catch (ValueCompareException e) { } catch (ValueCompareException e) {
return false; return false;
} catch (PathNotFoundException e) { } catch (PathNotFoundException e) {
@ -374,6 +381,9 @@ public class Criteria implements Predicate {
*/ */
public static Criteria where(String key) { public static Criteria where(String key) {
if(!key.startsWith("$") && !key.startsWith("@")){
key = "@." + key;
}
return where(PathCompiler.compile(key)); return where(PathCompiler.compile(key));
} }
@ -384,6 +394,9 @@ public class Criteria implements Predicate {
* @return the criteria builder * @return the criteria builder
*/ */
public Criteria and(String key) { public Criteria and(String key) {
if(!key.startsWith("$") && !key.startsWith("@")){
key = "@." + key;
}
return new Criteria(this.criteriaChain, PathCompiler.compile(key)); return new Criteria(this.criteriaChain, PathCompiler.compile(key));
} }
@ -395,7 +408,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria is(Object o) { public Criteria is(Object o) {
this.criteriaType = CriteriaType.EQ; this.criteriaType = CriteriaType.EQ;
this.expected = o; this.right = o;
return this; return this;
} }
@ -417,7 +430,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria ne(Object o) { public Criteria ne(Object o) {
this.criteriaType = CriteriaType.NE; this.criteriaType = CriteriaType.NE;
this.expected = o; this.right = o;
return this; return this;
} }
@ -429,7 +442,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria lt(Object o) { public Criteria lt(Object o) {
this.criteriaType = CriteriaType.LT; this.criteriaType = CriteriaType.LT;
this.expected = o; this.right = o;
return this; return this;
} }
@ -441,7 +454,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria lte(Object o) { public Criteria lte(Object o) {
this.criteriaType = CriteriaType.LTE; this.criteriaType = CriteriaType.LTE;
this.expected = o; this.right = o;
return this; return this;
} }
@ -453,7 +466,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria gt(Object o) { public Criteria gt(Object o) {
this.criteriaType = CriteriaType.GT; this.criteriaType = CriteriaType.GT;
this.expected = o; this.right = o;
return this; return this;
} }
@ -465,7 +478,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria gte(Object o) { public Criteria gte(Object o) {
this.criteriaType = CriteriaType.GTE; this.criteriaType = CriteriaType.GTE;
this.expected = o; this.right = o;
return this; return this;
} }
@ -478,7 +491,7 @@ public class Criteria implements Predicate {
public Criteria regex(Pattern pattern) { public Criteria regex(Pattern pattern) {
notNull(pattern, "pattern can not be null"); notNull(pattern, "pattern can not be null");
this.criteriaType = CriteriaType.REGEX; this.criteriaType = CriteriaType.REGEX;
this.expected = pattern; this.right = pattern;
return this; return this;
} }
@ -503,7 +516,7 @@ public class Criteria implements Predicate {
public Criteria in(Collection<?> c) { public Criteria in(Collection<?> c) {
notNull(c, "collection can not be null"); notNull(c, "collection can not be null");
this.criteriaType = CriteriaType.IN; this.criteriaType = CriteriaType.IN;
this.expected = c; this.right = c;
return this; return this;
} }
@ -528,7 +541,7 @@ public class Criteria implements Predicate {
public Criteria nin(Collection<?> c) { public Criteria nin(Collection<?> c) {
notNull(c, "collection can not be null"); notNull(c, "collection can not be null");
this.criteriaType = CriteriaType.NIN; this.criteriaType = CriteriaType.NIN;
this.expected = c; this.right = c;
return this; return this;
} }
@ -553,7 +566,7 @@ public class Criteria implements Predicate {
public Criteria all(Collection<?> c) { public Criteria all(Collection<?> c) {
notNull(c, "collection can not be null"); notNull(c, "collection can not be null");
this.criteriaType = CriteriaType.ALL; this.criteriaType = CriteriaType.ALL;
this.expected = c; this.right = c;
return this; return this;
} }
@ -570,7 +583,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria size(int size) { public Criteria size(int size) {
this.criteriaType = CriteriaType.SIZE; this.criteriaType = CriteriaType.SIZE;
this.expected = size; this.right = size;
return this; return this;
} }
@ -583,7 +596,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria exists(boolean b) { public Criteria exists(boolean b) {
this.criteriaType = CriteriaType.EXISTS; this.criteriaType = CriteriaType.EXISTS;
this.expected = b; this.right = b;
return this; return this;
} }
@ -596,7 +609,7 @@ public class Criteria implements Predicate {
public Criteria type(Class<?> t) { public Criteria type(Class<?> t) {
notNull(t, "type can not be null"); notNull(t, "type can not be null");
this.criteriaType = CriteriaType.TYPE; this.criteriaType = CriteriaType.TYPE;
this.expected = t; this.right = t;
return this; return this;
} }
@ -607,7 +620,7 @@ public class Criteria implements Predicate {
*/ */
public Criteria notEmpty() { public Criteria notEmpty() {
this.criteriaType = CriteriaType.NOT_EMPTY; this.criteriaType = CriteriaType.NOT_EMPTY;
this.expected = null; this.right = null;
return this; return this;
} }
@ -619,73 +632,115 @@ public class Criteria implements Predicate {
*/ */
public Criteria matches(Predicate p) { public Criteria matches(Predicate p) {
this.criteriaType = CriteriaType.MATCHES; this.criteriaType = CriteriaType.MATCHES;
this.expected = p; this.right = p;
return this; return this;
} }
private static boolean isPath(String string){
return (string != null && (string.startsWith("$") || string.startsWith("@")));
}
private static boolean isString(String string){
return (string != null && !string.isEmpty() && string.charAt(0) == '\'' && string.charAt(string.length() - 1) == '\'');
}
/**
* Parse the provided criteria
* @param criteria
* @return a criteria
*/
public static Criteria parse(String criteria){
int operatorIndex = -1;
String left = "";
String operator = "";
String right = "";
for (int y = 0; y < OPERATORS.length; y++) {
operatorIndex = criteria.indexOf(OPERATORS[y]);
if (operatorIndex != -1) {
operator = OPERATORS[y];
break;
}
}
if (!operator.isEmpty()) {
left = criteria.substring(0, operatorIndex).trim();
right = criteria.substring(operatorIndex + operator.length()).trim();
} else {
left = criteria.trim();
}
return Criteria.create(left, operator, right);
}
/** /**
* Creates a new criteria * Creates a new criteria
* @param path path to evaluate in criteria * @param left path to evaluate in criteria
* @param operator operator * @param operator operator
* @param expected expected value * @param right expected value
* @return a new Criteria * @return a new Criteria
*/ */
public static Criteria create(String path, String operator, String expected) { public static Criteria create(String left, String operator, String right) {
if (!expected.isEmpty() && expected.charAt(0) == '\'' && expected.charAt(expected.length() - 1) == '\'') { Object leftPrepared = left;
expected = expected.substring(1, expected.length() - 1); Object rightPrepared = right;
Path leftPath = null;
Path rightPath = null;
if(isPath(left)){
leftPath = PathCompiler.compile(left);
if(!leftPath.isDefinite()){
throw new InvalidPathException("the predicate path: " + left + " is not definite");
}
leftPrepared = leftPath;
} else if(isString(left)) {
leftPrepared = left.substring(1, left.length() - 1);
} }
Path p = PathCompiler.compile(path); if(isPath(right)){
rightPath = PathCompiler.compile(right);
if (("$".equals(path) || "@".equals(path) )&& (operator == null || operator.isEmpty()) && (expected == null || expected.isEmpty())) { if(!rightPath.isDefinite()){
return new Criteria(p, CriteriaType.NE, null); throw new InvalidPathException("the predicate path: " + right + " is not definite");
} else if (operator.isEmpty()) {
return Criteria.where(path).exists(true);
} else {
if(expected.startsWith("$") || expected.startsWith("@")){
Path compile = PathCompiler.compile(expected);
if(!compile.isDefinite()){
throw new InvalidPathException("the predicate path: " + expected + " is not definite");
}
return new Criteria(p, CriteriaType.parse(operator), compile);
} else {
return new Criteria(p, CriteriaType.parse(operator), expected);
} }
rightPrepared = rightPath;
} else if(isString(right)) {
rightPrepared = right.substring(1, right.length() - 1);
}
if(leftPath != null && operator.isEmpty()){
return Criteria.where(leftPath).exists(true);
} else {
return new Criteria(leftPrepared, CriteriaType.parse(operator), rightPrepared);
} }
} }
private static int safeCompare(Object expected, Object providerParsed) throws ValueCompareException { private static int safeCompare(Object left, Object right) throws ValueCompareException {
if(expected == providerParsed){ if(left == right){
return 0; return 0;
} }
boolean expNullish = isNullish(expected); boolean leftNullish = isNullish(left);
boolean provNullish = isNullish(providerParsed); boolean rightNullish = isNullish(right);
if (expNullish && !provNullish) { if (leftNullish && !rightNullish) {
return -1; return -1;
} else if (!expNullish && provNullish) { } else if (!leftNullish && rightNullish) {
return 1; return 1;
} else if (expNullish && provNullish) { } else if (leftNullish && rightNullish) {
return 0; return 0;
} else if (expected instanceof String && providerParsed instanceof String) { } else if (left instanceof String && right instanceof String) {
return ((String) expected).compareTo((String) providerParsed); return ((String) left).compareTo((String) right);
} else if (expected instanceof Number && providerParsed instanceof Number) { } else if (left instanceof Number && right instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(providerParsed.toString())); return new BigDecimal(left.toString()).compareTo(new BigDecimal(right.toString()));
} else if (expected instanceof String && providerParsed instanceof Number) { } else if (left instanceof String && right instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(providerParsed.toString())); return new BigDecimal(left.toString()).compareTo(new BigDecimal(right.toString()));
} else if (expected instanceof String && providerParsed instanceof Boolean) { } else if (left instanceof String && right instanceof Boolean) {
Boolean e = Boolean.valueOf((String) expected); Boolean e = Boolean.valueOf((String) left);
Boolean a = (Boolean) providerParsed; Boolean a = (Boolean) right;
return e.compareTo(a); return e.compareTo(a);
} else if (expected instanceof Boolean && providerParsed instanceof Boolean) { } else if (left instanceof Boolean && right instanceof Boolean) {
Boolean e = (Boolean) expected; Boolean e = (Boolean) left;
Boolean a = (Boolean) providerParsed; Boolean a = (Boolean) right;
return e.compareTo(a); return e.compareTo(a);
} else { } else {
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), providerParsed.getClass().getName()); logger.debug("Can not compare a {} with a {}", left.getClass().getName(), right.getClass().getName());
throw new ValueCompareException(); throw new ValueCompareException();
} }
} }
@ -699,16 +754,16 @@ public class Criteria implements Predicate {
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(path.toString()) sb.append(left.toString())
//.append("|") //.append("|")
.append(criteriaType.toString()) .append(criteriaType.toString())
//.append("|") //.append("|")
.append(wrapString(expected)); .append(wrapString(right));
//.append("|"); //.append("|");
return sb.toString(); return sb.toString();
} }
private String wrapString(Object o){ private static String wrapString(Object o){
if(o == null){ if(o == null){
return "null"; return "null";
} }
@ -721,25 +776,5 @@ public class Criteria implements Predicate {
public static Criteria parse(String criteria){
int operatorIndex = -1;
String left = "";
String operator = "";
String right = "";
for (int y = 0; y < OPERATORS.length; y++) {
operatorIndex = criteria.indexOf(OPERATORS[y]);
if (operatorIndex != -1) {
operator = OPERATORS[y];
break;
}
}
if (!operator.isEmpty()) {
left = criteria.substring(0, operatorIndex).trim();
right = criteria.substring(operatorIndex + operator.length()).trim();
} else {
left = criteria.trim();
}
return Criteria.create(left, operator, right);
}
} }

34
json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java

@ -75,4 +75,38 @@ public class InlineFilterTest extends BaseTest {
List h = reader.read("$.store.book[?(@.display-price == 8.95 || @.display-price == 8.99 || (@.display-price == 22.99 && @.category == 'reference') )].author", List.class); List h = reader.read("$.store.book[?(@.display-price == 8.95 || @.display-price == 8.99 || (@.display-price == 22.99 && @.category == 'reference') )].author", List.class);
assertThat(h).containsOnly("Nigel Rees", "Herman Melville"); assertThat(h).containsOnly("Nigel Rees", "Herman Melville");
} }
@Test
public void no_path_ref_in_filter_hit_all() {
List<String> res = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?('a' == 'a')].author");
assertThat(res).containsExactly("Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien");
}
@Test
public void no_path_ref_in_filter_hit_none() {
List<String> res = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?('a' == 'b')].author");
assertThat(res).isEmpty();
}
@Test
public void path_can_be_on_either_side_of_operator() {
List<String> resLeft = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.category == 'reference')].author");
List<String> resRight = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?('reference' == @.category)].author");
assertThat(resLeft).containsExactly("Nigel Rees");
assertThat(resRight).containsExactly("Nigel Rees");
}
@Test
public void path_can_be_on_both_side_of_operator() {
List<String> res = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.category == @.category)].author");
assertThat(res).containsExactly("Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien");
}
} }

6
json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java

@ -275,9 +275,9 @@ public class FilterTest extends BaseTest {
JsonPath.parse(json).json(); JsonPath.parse(json).json();
assertThat((String)JsonPath.read(jocksInTexas1, "[0].address.state"), is("Texas")); assertThat((String)JsonPath.read(jocksInTexas1, "$[0].address.state"), is("Texas"));
assertThat((String)JsonPath.read(jocksInTexas1, "[0].first-name"), is("Jock")); assertThat((String)JsonPath.read(jocksInTexas1, "$[0].first-name"), is("Jock"));
assertThat((String)JsonPath.read(jocksInTexas1, "[0].last-name"), is("Ewing")); assertThat((String)JsonPath.read(jocksInTexas1, "$[0].last-name"), is("Ewing"));
} }

Loading…
Cancel
Save