Browse Source

Implement the field exists check.

pull/41/head
Kalle Stenflo 10 years ago
parent
commit
143f6ff25d
  1. 785
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  2. 618
      json-path/src/main/java/com/jayway/jsonpath/Criteria2.java
  3. 178
      json-path/src/main/java/com/jayway/jsonpath/Filter.java
  4. 54
      json-path/src/main/java/com/jayway/jsonpath/Filter2.java
  5. 14
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  6. 4
      json-path/src/main/java/com/jayway/jsonpath/ReadContext.java
  7. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java
  8. 9
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/compiler/FilterPathToken.java
  9. 23
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/compiler/PathCompiler.java
  10. 41
      json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java
  11. 4
      json-path/src/test/java/com/jayway/jsonpath/Filter2Test.java
  12. 16
      json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
  13. 2
      json-path/src/test/java/com/jayway/jsonpath/IssuesTest.java

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

@ -1,361 +1,296 @@
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.spi.compiler.PathCompiler;
import com.jayway.jsonpath.spi.compiler.Path;
import com.jayway.jsonpath.internal.Utils;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.Utils.isNumeric;
import static com.jayway.jsonpath.internal.Utils.join;
import static com.jayway.jsonpath.internal.Utils.notNull;
import static java.util.Arrays.asList;
/**
* @author Kalle Stenflo
*
*/
public class Criteria {
public class Criteria implements Predicate {
private static final Logger logger = LoggerFactory.getLogger(Criteria.class);
private enum CriteriaType {
GT,
GTE,
LT,
LTE,
NE,
IN,
NIN,
ALL,
SIZE,
EXISTS,
TYPE,
REGEX,
NOT_EMPTY,
OR
}
/**
* Custom "not-null" object as we have to be able to work with {@literal null} values as well.
*/
private static final Object NOT_SET = new Object();
//private final JsonPath key;
//private final Path key;
private final Path key;
private final Path path;
private CriteriaType criteriaType;
private Object expected;
private final List<Criteria> criteriaChain;
private final LinkedHashMap<CriteriaType, Object> criteria = new LinkedHashMap<CriteriaType, Object>();
private Object isValue = NOT_SET;
private Criteria(String key) {
Utils.notEmpty(key, "key can not be null or empty");
this.criteriaChain = new ArrayList<Criteria>();
this.criteriaChain.add(this);
this.key = PathCompiler.tokenize(key);
}
private Criteria(Path path) {
Utils.notNull(path, "path can not be null");
this.criteriaChain = new ArrayList<Criteria>();
this.criteriaChain.add(this);
this.key = path;
}
private Criteria(List<Criteria> criteriaChain, String key) {
Utils.notEmpty(key, "key can not be null or empty");
this.criteriaChain = criteriaChain;
//this.key = JsonPath.compile(key);
this.criteriaChain.add(this);
//this.key = PathCompiler.compile(key);
this.key = PathCompiler.tokenize(key);
}
//JsonPath getKey() {
//Path getKey() {
Path getKey() {
return this.key;
}
/**
* Checks if this criteria matches the given map
*
* @param map map to check
* @return true if criteria is a match
*/
boolean matches(Object map, Configuration configuration) {
for (Criteria c : this.criteriaChain) {
if (!c.singleObjectApply(map, configuration)) {
return false;
private enum CriteriaType {
EQ {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = (0 == safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
}
return true;
}
//private static Object readSafely(JsonPath path, Map<String, Object> map) {
//private static Object readSafely(Path path, Object map) {
private static Object readSafely(Path path, Object map) {
try {
return path.evaluate(map, Configuration.defaultConfiguration()).get();
//return PathEvaluator.evaluate(path, map, JsonProviderFactory.createProvider(), Collections.EMPTY_SET).getResult();
//return path. read(map);
} catch (InvalidPathException e) {
return null;
}
}
private static <T> boolean objectOrAnyCollectionItemMatches(final Object singleObjectOrCollection,
final Predicate<T> predicate) {
if (singleObjectOrCollection instanceof Collection) {
Iterator it = ((Collection) singleObjectOrCollection).iterator();
while (it.hasNext()) {
if (predicate.accept((T) it.next())) {
return true;
}
},
NE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = (0 != safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
return false;
}
return predicate.accept((T) singleObjectOrCollection);
}
private boolean singleObjectApply(Object map, final Configuration configuration) {
for (CriteriaType key : this.criteria.keySet()) {
final Object actualVal = readSafely(this.key, map);
final Object expectedVal = this.criteria.get(key);
if (CriteriaType.GT.equals(key)) {
if (expectedVal == null || actualVal == null) {
},
GT {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
final Number expectedNumber = (Number) expectedVal;
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Number>() {
@Override
public boolean accept(Number value) {
return (value.doubleValue() > expectedNumber.doubleValue());
}
});
} else if (CriteriaType.GTE.equals(key)) {
if (expectedVal == null || actualVal == null) {
boolean res = (0 > safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
GTE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
final Number expectedNumber = (Number) expectedVal;
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Number>() {
@Override
public boolean accept(Number value) {
return (value.doubleValue() >= expectedNumber.doubleValue());
}
});
} else if (CriteriaType.LT.equals(key)) {
if (expectedVal == null || actualVal == null) {
boolean res = (0 >= safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
LT {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
final Number expectedNumber = (Number) expectedVal;
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Number>() {
@Override
public boolean accept(Number value) {
return (value.doubleValue() < expectedNumber.doubleValue());
}
});
} else if (CriteriaType.LTE.equals(key)) {
if (expectedVal == null || actualVal == null) {
boolean res = (0 < safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
LTE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
final Number expectedNumber = (Number) expectedVal;
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Number>() {
@Override
public boolean accept(Number value) {
return (value.doubleValue() <= expectedNumber.doubleValue());
boolean res = (0 <= safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
IN {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
Collection exps = (Collection) expected;
for (Object exp : exps) {
if (0 == safeCompare(exp, actual, configuration)) {
res = true;
break;
}
});
} else if (CriteriaType.NE.equals(key)) {
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Object>() {
@Override
public boolean accept(Object value) {
if (expectedVal == null && value == null) {
return false;
}
logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", exps), res);
return res;
}
},
NIN {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
Collection nexps = (Collection) expected;
boolean res = !nexps.contains(actual);
logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", nexps), res);
return res;
}
},
ALL {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = true;
Collection exps = (Collection) expected;
if (configuration.getProvider().isArray(actual)) {
for (Object exp : exps) {
boolean found = false;
for (Object check : configuration.getProvider().toIterable(actual)) {
if (0 == safeCompare(exp, check, configuration)) {
found = true;
break;
}
}
if (expectedVal == null) {
return true;
} else {
return !expectedVal.equals(value);
if (!found) {
res = false;
break;
}
}
});
} else if (CriteriaType.IN.equals(key)) {
Collection exp = (Collection) expectedVal;
return exp.contains(actualVal);
} else if (CriteriaType.NIN.equals(key)) {
Collection exp = (Collection) expectedVal;
return !exp.contains(actualVal);
} else if (CriteriaType.ALL.equals(key)) {
Collection exp = (Collection) expectedVal;
Collection act = (Collection) actualVal;
return act.containsAll(exp);
} else if (CriteriaType.SIZE.equals(key)) {
int exp = (Integer) expectedVal;
List act = (List) actualVal;
return (act.size() == exp);
} else if (CriteriaType.NOT_EMPTY.equals(key)) {
if (actualVal == null) {
return false;
logger.debug("[{}] {} [{}] => {}", join(", ", configuration.getProvider().toIterable(actual)), name(), join(", ", exps), res);
} else {
res = false;
logger.debug("[{}] {} [{}] => {}", "<NOT AN ARRAY>", name(), join(", ", exps), res);
}
boolean empty = false;
if (actualVal instanceof Collection) {
empty = ((List) actualVal).isEmpty();
} else if (actualVal instanceof String) {
empty = ((String) actualVal).isEmpty();
return res;
}
},
SIZE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
int size = (Integer) expected;
boolean res;
if (configuration.getProvider().isArray(actual)) {
int length = configuration.getProvider().length(actual);
res = length == size;
logger.debug("Array with size {} {} {} => {}", length, name(), size, res);
} else if (actual instanceof String) {
int length = ((String) actual).length();
res = length == size;
logger.debug("String with length {} {} {} => {}", length, name(), size, res);
} else {
res = false;
logger.debug("{} {} {} => {}", actual == null ? "null" : actual.getClass().getName(), name(), size, res);
}
return !empty;
} else if (CriteriaType.EXISTS.equals(key)) {
if (configuration.getProvider().isArray(map)) {
return false;
return res;
}
},
EXISTS {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
//This must be handled outside
throw new UnsupportedOperationException();
}
},
TYPE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
final Class<?> expType = (Class<?>) expected;
final Class<?> actType = actual == null ? null : actual.getClass();
if (actType != null) {
return expType.isAssignableFrom(actType);
}
final boolean exp = (Boolean) expectedVal;
return objectOrAnyCollectionItemMatches(map, new Predicate<Object>() {
@Override
public boolean accept(final Object value) {
boolean act = true;
Object res;
try {
Set<Option> options = new HashSet<Option>() {{
add(Option.THROW_ON_MISSING_PROPERTY);
}};
//res = getKey().read(value, configuration.options(Option.THROW_ON_MISSING_PROPERTY));
//res = PathEvaluator.evaluate(getKey(), value, configuration.getProvider(), options).getResult();
res = getKey().evaluate(value, Configuration.defaultConfiguration().options(Option.THROW_ON_MISSING_PROPERTY)).get();
if (configuration.getProvider().isArray(res)) {
//if(getKey().isDefinite()){
//if(getKey().isDefinite()){
if (getKey().isDefinite()) {
act = true;
} else {
act = (configuration.getProvider().length(res) > 0);
}
}
} catch (InvalidPathException e) {
act = false;
}
return act == exp;
}
});
} else if (CriteriaType.TYPE.equals(key)) {
final Class<?> exp = (Class<?>) expectedVal;
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Object>() {
@Override
public boolean accept(Object value) {
Class<?> act = value == null ? null : value.getClass();
if (act == null) {
return false;
} else {
return act.equals(exp);
}
return false;
}
},
REGEX {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
final Pattern pattern = (Pattern) expected;
if (actual != null && actual instanceof String) {
res = pattern.matcher(actual.toString()).matches();
}
logger.debug("[{}] {} [{}] => {}", actual, name(), expected.toString(), res);
return res;
}
},
MATCHES {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
Predicate exp = (Predicate) expected;
return exp.apply(actual, configuration);
}
},
NOT_EMPTY {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
if (actual != null) {
if (configuration.getProvider().isArray(actual)) {
int len = configuration.getProvider().length(actual);
res = (0 != len);
logger.debug("array length = {} {} => {}", len, name(), res);
} else if (actual instanceof String) {
int len = ((String) actual).length();
res = (0 != len);
logger.debug("string length = {} {} => {}", len, name(), res);
}
});
}
return res;
}
};
abstract boolean eval(Object expected, Object actual, Configuration configuration);
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 {
throw new UnsupportedOperationException("CriteriaType " + str + " can not be parsed");
}
}
}
} else if (CriteriaType.REGEX.equals(key)) {
final Pattern exp = (Pattern) expectedVal;
private Criteria(List<Criteria> criteriaChain, Path path) {
if (!path.isDefinite()) {
throw new InvalidCriteriaException("A criteria path must be definite. The path " + path.toString() + " is not!");
}
this.path = path;
this.criteriaChain = criteriaChain;
this.criteriaChain.add(this);
}
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<String>() {
private Criteria(Path path) {
this(new LinkedList<Criteria>(), path);
}
@Override
public boolean accept(String value) {
return value != null && exp.matcher(value).matches();
}
});
private Criteria(Path path, CriteriaType criteriaType, Object expected) {
this(new LinkedList<Criteria>(), path);
this.criteriaType = criteriaType;
this.expected = expected;
}
} else {
throw new UnsupportedOperationException("Criteria type not supported: " + key.name());
@Override
public boolean apply(Object model, Configuration configuration) {
for (Criteria criteria : criteriaChain) {
if (!criteria.eval(model, configuration)) {
return false;
}
}
if (isValue != NOT_SET) {
if (isValue instanceof Collection) {
Collection<Criteria> cs = (Collection<Criteria>) isValue;
for (Criteria crit : cs) {
for (Criteria c : crit.criteriaChain) {
if (!c.singleObjectApply(map, configuration)) {
return false;
}
}
}
return true;
} else {
Object actualVal = readSafely(this.key, map);
return objectOrAnyCollectionItemMatches(actualVal, new Predicate<Object>() {
@Override
public boolean accept(Object value) {
if (isValue == null) {
return value == null;
} else {
return isValue.equals(value);
}
}
return true;
}
});
private boolean eval(Object model, Configuration configuration) {
if (CriteriaType.EXISTS == criteriaType) {
boolean exists = ((Boolean) expected);
try {
path.evaluate(model, configuration.options(Option.THROW_ON_MISSING_PROPERTY)).get();
return exists == true;
} catch (PathNotFoundException e) {
return exists == false;
}
} else {
try {
final Object actual = path.evaluate(model, configuration).get();
return criteriaType.eval(expected, actual, configuration);
} catch (CompareException e) {
return false;
} catch (PathNotFoundException e) {
return false;
}
}
return true;
}
/**
* Static factory method to create a Criteria using the provided key
*
@ -374,7 +309,7 @@ public class Criteria {
*/
public static Criteria where(String key) {
return new Criteria(key);
return where(PathCompiler.tokenize(key));
}
/**
@ -384,7 +319,7 @@ public class Criteria {
* @return the criteria builder
*/
public Criteria and(String key) {
return new Criteria(this.criteriaChain, key);
return new Criteria(this.criteriaChain, PathCompiler.tokenize(key));
}
/**
@ -394,14 +329,8 @@ public class Criteria {
* @return
*/
public Criteria is(Object o) {
if (isValue != NOT_SET) {
throw new InvalidCriteriaException(
"Multiple 'is' values declared. You need to use 'and' with multiple criteria");
}
if (this.criteria.size() > 0 && "$not".equals(this.criteria.keySet().toArray()[this.criteria.size() - 1])) {
throw new InvalidCriteriaException("Invalid query: 'not' can't be used with 'is' - use 'ne' instead.");
}
this.isValue = o;
this.criteriaType = CriteriaType.TYPE.EQ;
this.expected = o;
return this;
}
@ -422,7 +351,8 @@ public class Criteria {
* @return
*/
public Criteria ne(Object o) {
criteria.put(CriteriaType.NE, o);
this.criteriaType = CriteriaType.TYPE.NE;
this.expected = o;
return this;
}
@ -433,7 +363,8 @@ public class Criteria {
* @return
*/
public Criteria lt(Object o) {
criteria.put(CriteriaType.LT, o);
this.criteriaType = CriteriaType.TYPE.LT;
this.expected = o;
return this;
}
@ -444,7 +375,8 @@ public class Criteria {
* @return
*/
public Criteria lte(Object o) {
criteria.put(CriteriaType.LTE, o);
this.criteriaType = CriteriaType.TYPE.LTE;
this.expected = o;
return this;
}
@ -455,7 +387,8 @@ public class Criteria {
* @return
*/
public Criteria gt(Object o) {
criteria.put(CriteriaType.GT, o);
this.criteriaType = CriteriaType.TYPE.GT;
this.expected = o;
return this;
}
@ -466,7 +399,21 @@ public class Criteria {
* @return
*/
public Criteria gte(Object o) {
criteria.put(CriteriaType.GTE, o);
this.criteriaType = CriteriaType.TYPE.GTE;
this.expected = o;
return this;
}
/**
* Creates a criterion using a Regex
*
* @param pattern
* @return
*/
public Criteria regex(Pattern pattern) {
notNull(pattern, "pattern can not be null");
this.criteriaType = CriteriaType.TYPE.REGEX;
this.expected = pattern;
return this;
}
@ -478,10 +425,6 @@ public class Criteria {
* @return
*/
public Criteria in(Object... o) {
if (o.length > 1 && o[1] instanceof Collection) {
throw new InvalidCriteriaException("You can only pass in one argument of type "
+ o[1].getClass().getName());
}
return in(Arrays.asList(o));
}
@ -494,8 +437,8 @@ public class Criteria {
*/
public Criteria in(Collection<?> c) {
notNull(c, "collection can not be null");
checkFilterCanBeApplied(CriteriaType.IN);
criteria.put(CriteriaType.IN, c);
this.criteriaType = CriteriaType.TYPE.IN;
this.expected = c;
return this;
}
@ -519,12 +462,11 @@ public class Criteria {
*/
public Criteria nin(Collection<?> c) {
notNull(c, "collection can not be null");
checkFilterCanBeApplied(CriteriaType.NIN);
criteria.put(CriteriaType.NIN, c);
this.criteriaType = CriteriaType.TYPE.NIN;
this.expected = c;
return this;
}
/**
* The <code>all</code> operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
@ -545,38 +487,28 @@ public class Criteria {
*/
public Criteria all(Collection<?> c) {
notNull(c, "collection can not be null");
checkFilterCanBeApplied(CriteriaType.ALL);
criteria.put(CriteriaType.ALL, c);
this.criteriaType = CriteriaType.TYPE.ALL;
this.expected = c;
return this;
}
/**
* The <code>size</code> operator matches:
*
* <p/>
* <ol>
* <li>array with the specified number of elements.</li>
* <li>string with given length.</li>
* <li>array with the specified number of elements.</li>
* <li>string with given length.</li>
* </ol>
*
* @param size
* @return
*/
public Criteria size(int size) {
checkFilterCanBeApplied(CriteriaType.SIZE);
criteria.put(CriteriaType.SIZE, size);
this.criteriaType = CriteriaType.TYPE.SIZE;
this.expected = size;
return this;
}
/**
* The <code>notEmpty</code> operator checks that an array is not empty.
*
* @return
*/
public Criteria notEmpty() {
checkFilterCanBeApplied(CriteriaType.NOT_EMPTY);
criteria.put(CriteriaType.NOT_EMPTY, null);
return this;
}
/**
* Check for existence (or lack thereof) of a field.
@ -585,7 +517,8 @@ public class Criteria {
* @return
*/
public Criteria exists(boolean b) {
criteria.put(CriteriaType.EXISTS, b);
this.criteriaType = CriteriaType.TYPE.EXISTS;
this.expected = b;
return this;
}
@ -597,120 +530,94 @@ public class Criteria {
*/
public Criteria type(Class<?> t) {
notNull(t, "type can not be null");
criteria.put(CriteriaType.TYPE, t);
this.criteriaType = CriteriaType.TYPE.TYPE;
this.expected = t;
return this;
}
/**
* Creates a criterion using a Regex
* The <code>notEmpty</code> operator checks that an array or String is not empty.
*
* @param pattern
* @return
*/
public Criteria regex(Pattern pattern) {
notNull(pattern, "pattern can not be null");
criteria.put(CriteriaType.REGEX, pattern);
public Criteria notEmpty() {
this.criteriaType = CriteriaType.TYPE.NOT_EMPTY;
this.expected = null;
return this;
}
public Criteria matches(String operator, String expected) {
Object check;
/**
* The <code>matches</code> operator checks that an object matches the given predicate.
*
* @return
*/
public Criteria matches(Predicate p) {
this.criteriaType = CriteriaType.TYPE.MATCHES;
this.expected = p;
return this;
}
if (expected.startsWith("'") && expected.endsWith("'")) {
check = expected.substring(1, expected.length() - 1);
} else if ("true".equals(expected)) {
check = Boolean.TRUE;
} else if ("false".equals(expected)) {
check = Boolean.FALSE;
} else if ("null".equals(expected)) {
check = null;
} else if (isNumeric(expected)) {
if (expected.contains(".")) {
check = Double.parseDouble(expected);
} else {
check = Integer.parseInt(expected);
}
} else {
throw new UnsupportedOperationException("Type not supported: " + expected);
}
if ("==".equals(operator)) {
return is(check);
} else if (">".equals(operator)) {
return gt(check);
} else if (">=".equals(operator)) {
return gte(check);
} else if ("<".equals(operator)) {
return lt(check);
} else if ("<=".equals(operator)) {
return lte(check);
} else if ("!=".equals(operator)) {
return ne(check);
} else if ("<>".equals(operator)) {
return ne(check);
private static int safeCompare(Object expected, Object actual, Configuration configuration) {
if (isNullish(expected) && !isNullish(actual)) {
return -1;
} else if (!isNullish(expected) && isNullish(actual)) {
return 1;
} else if (isNullish(expected) && isNullish(actual)) {
return 0;
} else if (expected instanceof String && actual instanceof String) {
return ((String) expected).compareTo((String) actual);
} else if (expected instanceof Number && actual instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString()));
} else if (expected instanceof String && actual instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString()));
} else if (expected instanceof String && actual instanceof Boolean) {
Boolean e = Boolean.valueOf((String)expected);
Boolean a = (Boolean) actual;
return e.compareTo(a);
} else if (expected instanceof Boolean && actual instanceof Boolean) {
Boolean e = (Boolean) expected;
Boolean a = (Boolean) actual;
return e.compareTo(a);
} else {
throw new UnsupportedOperationException("Operator not supported: " + operator);
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), actual.getClass().getName());
throw new CompareException();
}
}
/**
* Creates an 'or' criteria using the $or operator for all of the provided criteria
*
* @param criteria
*/
/*
public Criteria orOperator(Criteria... criteria) {
criteriaChain.add(new Criteria("$or").is(asList(criteria)));
return this;
private static boolean isNullish(Object o){
return (o == null || ((o instanceof String) && ("null".equals(o))));
}
*/
/**
* Creates a 'nor' criteria using the $nor operator for all of the provided criteria
*
* @param criteria
*/
/*
public Criteria norOperator(Criteria... criteria) {
criteriaChain.add(new Criteria("$nor").is(asList(criteria)));
return this;
}*/
/**
* Creates an 'and' criteria using the $and operator for all of the provided criteria
*
* @param criteria
*/
public Criteria andOperator(Criteria... criteria) {
criteriaChain.add(new Criteria("$and").is(asList(criteria)));
return this;
private static class CompareException extends RuntimeException {
}
private void checkFilterCanBeApplied(CriteriaType type) {
//if (getKey().getTokenizer().size() > 2) {
//if(getKey().getComponents().length > 2) {
if (getKey().tokenCount() > 2) {
throw new IllegalArgumentException("Cannot use " + type + " filter on a multi-level path expression");
public static Criteria create(String path, String operator, String expected) {
if (expected.startsWith("'") && expected.endsWith("'")) {
expected = expected.substring(1, expected.length() - 1);
}
}
Path p = PathCompiler.tokenize(path);
private interface Predicate<T> {
boolean accept(T value);
if("$".equals(path) && (operator == null || operator.isEmpty()) && (expected == null || expected.isEmpty()) ){
return new Criteria(p, CriteriaType.NE, null);
} else if (operator.isEmpty()) {
return Criteria.where(path).exists(true);
} else {
return new Criteria(p, CriteriaType.parse(operator), expected);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Iterator<Criteria> iterator = criteriaChain.iterator();
Criteria c = iterator.next();
if (c.isValue != NOT_SET) {
sb.append("@").append(key.toString().substring(1) + " == " + ((c.isValue instanceof String) ? ("'" + c.isValue + "'") : c.isValue.toString()));
}
sb.append(path.toString())
.append("|")
.append(criteriaType.name())
.append("|")
.append(expected)
.append("|");
return sb.toString();
}
}

618
json-path/src/main/java/com/jayway/jsonpath/Criteria2.java

@ -1,618 +0,0 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.spi.compiler.PathCompiler;
import com.jayway.jsonpath.spi.compiler.EvaluationContext;
import com.jayway.jsonpath.spi.compiler.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.Utils.join;
import static com.jayway.jsonpath.internal.Utils.notNull;
/**
*
*/
public class Criteria2 implements Predicate {
private static final Logger logger = LoggerFactory.getLogger(Criteria2.class);
private final Path path;
private CriteriaType criteriaType;
private Object expected;
private final List<Criteria2> criteriaChain;
private enum CriteriaType {
EQ {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = (0 == safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
NE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = (0 != safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
GT {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 > safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
GTE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 >= safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
LT {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 < safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
LTE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 <= safeCompare(expected, actual, configuration));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
},
IN {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
Collection exps = (Collection) expected;
for (Object exp : exps) {
if (0 == safeCompare(exp, actual, configuration)) {
res = true;
break;
}
}
logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", exps), res);
return res;
}
},
NIN {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
Collection nexps = (Collection) expected;
boolean res = !nexps.contains(actual);
logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", nexps), res);
return res;
}
},
ALL {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = true;
Collection exps = (Collection) expected;
if (configuration.getProvider().isArray(actual)) {
for (Object exp : exps) {
boolean found = false;
for (Object check : configuration.getProvider().toIterable(actual)) {
if (0 == safeCompare(exp, check, configuration)) {
found = true;
break;
}
}
if (!found) {
res = false;
break;
}
}
logger.debug("[{}] {} [{}] => {}", join(", ", configuration.getProvider().toIterable(actual)), name(), join(", ", exps), res);
} else {
res = false;
logger.debug("[{}] {} [{}] => {}", "<NOT AN ARRAY>", name(), join(", ", exps), res);
}
return res;
}
},
SIZE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
int size = (Integer) expected;
boolean res;
if (configuration.getProvider().isArray(actual)) {
int length = configuration.getProvider().length(actual);
res = length == size;
logger.debug("Array with size {} {} {} => {}", length, name(), size, res);
} else if (actual instanceof String) {
int length = ((String) actual).length();
res = length == size;
logger.debug("String with length {} {} {} => {}", length, name(), size, res);
} else {
res = false;
logger.debug("{} {} {} => {}", actual == null ? "null" : actual.getClass().getName(), name(), size, res);
}
return res;
}
},
EXISTS {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
//This must be handled outside
throw new UnsupportedOperationException();
}
},
TYPE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
final Class<?> expType = (Class<?>) expected;
final Class<?> actType = actual == null ? null : actual.getClass();
if (actType != null) {
return expType.isAssignableFrom(actType);
}
return false;
}
},
REGEX {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
final Pattern pattern = (Pattern) expected;
if (actual != null && actual instanceof String) {
res = pattern.matcher(actual.toString()).matches();
}
logger.debug("[{}] {} [{}] => {}", actual, name(), expected.toString(), res);
return res;
}
},
MATCHES {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
Predicate exp = (Predicate) expected;
return exp.apply(actual, configuration);
}
},
NOT_EMPTY {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
if (actual != null) {
if (configuration.getProvider().isArray(actual)) {
int len = configuration.getProvider().length(actual);
res = (0 != len);
logger.debug("array length = {} {} => {}", len, name(), res);
} else if (actual instanceof String) {
int len = ((String) actual).length();
res = (0 != len);
logger.debug("string length = {} {} => {}", len, name(), res);
}
}
return res;
}
};
abstract boolean eval(Object expected, Object actual, Configuration configuration);
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 {
throw new UnsupportedOperationException("CriteriaType " + str + " can not be parsed");
}
}
}
private Criteria2(List<Criteria2> criteriaChain, Path path) {
if (!path.isDefinite()) {
throw new InvalidCriteriaException("A criteria path must be definite. The path " + path.toString() + " is not!");
}
this.path = path;
this.criteriaChain = criteriaChain;
this.criteriaChain.add(this);
}
private Criteria2(Path path) {
this(new LinkedList<Criteria2>(), path);
}
private Criteria2(Path path, CriteriaType criteriaType, Object expected) {
this(new LinkedList<Criteria2>(), path);
this.criteriaType = criteriaType;
this.expected = expected;
}
@Override
public boolean apply(Object model, Configuration configuration) {
for (Criteria2 criteria : criteriaChain) {
if (!criteria.eval(model, configuration)) {
return false;
}
}
return true;
}
private boolean eval(Object model, Configuration configuration) {
if (CriteriaType.EXISTS == criteriaType) {
boolean exists = ((Boolean) expected);
try {
path.evaluate(model, configuration.options(Option.THROW_ON_MISSING_PROPERTY)).get();
return exists == true;
} catch (PathNotFoundException e) {
return exists == false;
}
} else {
try {
final Object actual = path.evaluate(model, configuration).get();
return criteriaType.eval(expected, actual, configuration);
} catch (CompareException e) {
return false;
} catch (PathNotFoundException e) {
return false;
}
}
}
/**
* Static factory method to create a Criteria using the provided key
*
* @param key filed name
* @return the new criteria
*/
public static Criteria2 where(Path key) {
return new Criteria2(key);
}
/**
* Static factory method to create a Criteria using the provided key
*
* @param key filed name
* @return the new criteria
*/
public static Criteria2 where(String key) {
return where(PathCompiler.tokenize(key));
}
/**
* Static factory method to create a Criteria using the provided key
*
* @param key ads new filed to criteria
* @return the criteria builder
*/
public Criteria2 and(String key) {
return new Criteria2(this.criteriaChain, PathCompiler.tokenize(key));
}
/**
* Creates a criterion using equality
*
* @param o
* @return
*/
public Criteria2 is(Object o) {
this.criteriaType = CriteriaType.TYPE.EQ;
this.expected = o;
return this;
}
/**
* Creates a criterion using equality
*
* @param o
* @return
*/
public Criteria2 eq(Object o) {
return is(o);
}
/**
* Creates a criterion using the <b>!=</b> operator
*
* @param o
* @return
*/
public Criteria2 ne(Object o) {
this.criteriaType = CriteriaType.TYPE.NE;
this.expected = o;
return this;
}
/**
* Creates a criterion using the <b>&lt;</b> operator
*
* @param o
* @return
*/
public Criteria2 lt(Object o) {
this.criteriaType = CriteriaType.TYPE.LT;
this.expected = o;
return this;
}
/**
* Creates a criterion using the <b>&lt;=</b> operator
*
* @param o
* @return
*/
public Criteria2 lte(Object o) {
this.criteriaType = CriteriaType.TYPE.LTE;
this.expected = o;
return this;
}
/**
* Creates a criterion using the <b>&gt;</b> operator
*
* @param o
* @return
*/
public Criteria2 gt(Object o) {
this.criteriaType = CriteriaType.TYPE.GT;
this.expected = o;
return this;
}
/**
* Creates a criterion using the <b>&gt;=</b> operator
*
* @param o
* @return
*/
public Criteria2 gte(Object o) {
this.criteriaType = CriteriaType.TYPE.GTE;
this.expected = o;
return this;
}
/**
* Creates a criterion using a Regex
*
* @param pattern
* @return
*/
public Criteria2 regex(Pattern pattern) {
notNull(pattern, "pattern can not be null");
this.criteriaType = CriteriaType.TYPE.REGEX;
this.expected = pattern;
return this;
}
/**
* The <code>in</code> operator is analogous to the SQL IN modifier, allowing you
* to specify an array of possible matches.
*
* @param o the values to match against
* @return
*/
public Criteria2 in(Object... o) {
return in(Arrays.asList(o));
}
/**
* The <code>in</code> operator is analogous to the SQL IN modifier, allowing you
* to specify an array of possible matches.
*
* @param c the collection containing the values to match against
* @return
*/
public Criteria2 in(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = CriteriaType.TYPE.IN;
this.expected = c;
return this;
}
/**
* The <code>nin</code> operator is similar to $in except that it selects objects for
* which the specified field does not have any value in the specified array.
*
* @param o the values to match against
* @return
*/
public Criteria2 nin(Object... o) {
return nin(Arrays.asList(o));
}
/**
* The <code>nin</code> operator is similar to $in except that it selects objects for
* which the specified field does not have any value in the specified array.
*
* @param c the values to match against
* @return
*/
public Criteria2 nin(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = CriteriaType.TYPE.NIN;
this.expected = c;
return this;
}
/**
* The <code>all</code> operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
*
* @param o
* @return
*/
public Criteria2 all(Object... o) {
return all(Arrays.asList(o));
}
/**
* The <code>all</code> operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
*
* @param c
* @return
*/
public Criteria2 all(Collection<?> c) {
notNull(c, "collection can not be null");
this.criteriaType = CriteriaType.TYPE.ALL;
this.expected = c;
return this;
}
/**
* The <code>size</code> operator matches:
* <p/>
* <ol>
* <li>array with the specified number of elements.</li>
* <li>string with given length.</li>
* </ol>
*
* @param size
* @return
*/
public Criteria2 size(int size) {
this.criteriaType = CriteriaType.TYPE.SIZE;
this.expected = size;
return this;
}
/**
* Check for existence (or lack thereof) of a field.
*
* @param b
* @return
*/
public Criteria2 exists(boolean b) {
this.criteriaType = CriteriaType.TYPE.EXISTS;
this.expected = b;
return this;
}
/**
* The $type operator matches values based on their Java type.
*
* @param t
* @return
*/
public Criteria2 type(Class<?> t) {
notNull(t, "type can not be null");
this.criteriaType = CriteriaType.TYPE.TYPE;
this.expected = t;
return this;
}
/**
* The <code>notEmpty</code> operator checks that an array or String is not empty.
*
* @return
*/
public Criteria2 notEmpty() {
this.criteriaType = CriteriaType.TYPE.NOT_EMPTY;
this.expected = null;
return this;
}
/**
* The <code>matches</code> operator checks that an object matches the given predicate.
*
* @return
*/
public Criteria2 matches(Predicate p) {
this.criteriaType = CriteriaType.TYPE.MATCHES;
this.expected = p;
return this;
}
private static int safeCompare(Object expected, Object actual, Configuration configuration) {
if (expected == null && actual != null) {
return -1;
} else if (expected != null && actual == null) {
return 1;
} else if (expected == null && actual == null) {
return 0;
} else if (expected instanceof String && actual instanceof String) {
return ((String) expected).compareTo((String) actual);
} else if (expected instanceof Number && actual instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString()));
} else if (expected instanceof String && actual instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString()));
} else if (expected instanceof String && actual instanceof Boolean) {
Boolean e = Boolean.valueOf((String)expected);
Boolean a = (Boolean) actual;
return e.compareTo(a);
} else if (expected instanceof Boolean && actual instanceof Boolean) {
Boolean e = (Boolean) expected;
Boolean a = (Boolean) actual;
return e.compareTo(a);
} else {
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), actual.getClass().getName());
throw new CompareException();
}
}
private static class CompareException extends RuntimeException {
}
public static Criteria2 create(String path, String operator, String expected) {
if (expected.startsWith("'") && expected.endsWith("'")) {
expected = expected.substring(1, expected.length() - 1);
}
Path p = PathCompiler.tokenize(path);
if (operator.isEmpty()) {
return Criteria2.where(path).exists(true);
} else {
return new Criteria2(p, CriteriaType.parse(operator), expected);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(path.toString())
.append("|")
.append(criteriaType.name())
.append("|")
.append(expected)
.append("|");
return sb.toString();
}
}

178
json-path/src/main/java/com/jayway/jsonpath/Filter.java

@ -1,170 +1,52 @@
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.List;
/**
* A filter is used to filter the content of a JSON array in a JSONPath.
* <p/>
* Sample
* <p/>
* <code>
* String doc = {"items": [{"name" : "john"}, {"name": "bob"}]}
* <p/>
* List<String> names = JsonPath.read(doc, "$items[?].name", Filter.filter(Criteria.where("name").is("john"));
* </code>
*
* @author Kalle Stenflo
* @see Criteria
*/
public abstract class Filter<T> {
public class Filter implements Predicate {
/**
* Creates a new filter based on given criteria
*
* @param criteria the filter criteria
* @return a new filter
*/
public static Filter filter(Criteria criteria) {
return new MapFilter(criteria);
}
private List<Criteria> criteriaList = new ArrayList<Criteria>();
/**
* Filters the provided list based on this filter configuration
*
* @param filterItems items to filter
* @param configuration the json provider configuration that is used to create the result list
* @return the filtered list
*/
public Object doFilter(Iterable<T> filterItems, Configuration configuration) {
JsonProvider provider = configuration.getProvider();
Object result = provider.createArray();
for (T filterItem : filterItems) {
if (accept(filterItem, configuration)) {
provider.setProperty(result, provider.length(result), filterItem);
}
}
return result;
private Filter(Criteria criteria) {
this.criteriaList.add(criteria);
}
/**
* Check if this filter will accept or reject the given object
*
* @param obj item to check
* @return true if filter matches
*/
public abstract boolean accept(T obj);
/**
* Check if this filter will accept or reject the given object
*
* @param obj item to check
* @param configuration
* @return true if filter matches
*/
public abstract boolean accept(T obj, Configuration configuration);
/**
* Adds a new criteria to this filter
*
* @param criteria to add
* @return the updated filter
*/
public abstract Filter addCriteria(Criteria criteria);
// --------------------------------------------------------
//
// Default filter implementation
//
// --------------------------------------------------------
public static abstract class FilterAdapter<T> extends Filter<T> {
@Override
public boolean accept(T obj) {
return false;
}
@Override
public boolean accept(T obj, Configuration configuration) {
return accept(obj);
}
@Override
public Filter addCriteria(Criteria criteria) {
throw new UnsupportedOperationException("can not add criteria to a FilterAdapter.");
}
private Filter(List<Criteria> criteriaList) {
this.criteriaList = criteriaList;
}
private static class MapFilter extends FilterAdapter<Object> {
private HashMap<String, Criteria> criteria = new LinkedHashMap<String, Criteria>();
public MapFilter(Criteria criteria) {
addCriteria(criteria);
}
public MapFilter addCriteria(Criteria criteria) {
String key = criteria.getKey().toString();
Criteria existing = this.criteria.get(key);
if (existing == null) {
this.criteria.put(key, criteria);
} else {
existing.andOperator(criteria);
}
return this;
}
public static Filter filter(Criteria criteria) {
return new Filter(criteria);
}
@Override
public boolean accept(Object map) {
return accept(map, Configuration.defaultConfiguration());
}
public static Filter filter(List<Criteria> criteriaList) {
return new Filter(criteriaList);
}
@Override
public boolean accept(Object map, Configuration configuration) {
for (Criteria criterion : this.criteria.values()) {
if (!criterion.matches(map, configuration)) {
return false;
}
@Override
public boolean apply(Object target, Configuration configuration) {
for (Criteria criteria : criteriaList) {
if (!criteria.apply(target, configuration)) {
return false;
}
return true;
}
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[?(");
Iterator<Criteria> criteriaIterator = criteria.values().iterator();
Criteria criterion = criteriaIterator.next();
sb.append(criterion.toString());
public void addCriteria(Criteria criteria) {
criteriaList.add(criteria);
}
while (criteriaIterator.hasNext()) {
sb.append(" && ")
.append(criteriaIterator.next().toString());
}
sb.append(")]");
return sb.toString();
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Criteria crit : criteriaList) {
sb.append(crit.toString());
}
return sb.toString();
}
}

54
json-path/src/main/java/com/jayway/jsonpath/Filter2.java

@ -1,54 +0,0 @@
package com.jayway.jsonpath;
import java.util.ArrayList;
import java.util.List;
import static java.util.Arrays.asList;
/**
*
*/
public class Filter2 implements Predicate {
private List<Criteria2> criteriaList = new ArrayList<Criteria2>();
private Filter2(Criteria2 criteria) {
this.criteriaList.add(criteria);
}
private Filter2(List<Criteria2> criteriaList) {
this.criteriaList = criteriaList;
}
public static Filter2 filter(Criteria2 criteria) {
return new Filter2(criteria);
}
public static Filter2 filter(List<Criteria2> criteriaList) {
return new Filter2(criteriaList);
}
@Override
public boolean apply(Object target, Configuration configuration) {
for (Criteria2 criteria : criteriaList) {
if (!criteria.apply(target, configuration)) {
return false;
}
}
return true;
}
public void addCriteria(Criteria2 criteria) {
criteriaList.add(criteria);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Criteria2 crit : criteriaList) {
sb.append(crit.toString());
}
return sb.toString();
}
}

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

@ -99,7 +99,7 @@ public class JsonPath {
private final Path path;
private JsonPath(String jsonPath, Filter2[] filters) {
private JsonPath(String jsonPath, Filter[] filters) {
notNull(jsonPath, "path can not be null");
this.path = PathCompiler.tokenize(jsonPath, filters);
}
@ -372,7 +372,7 @@ public class JsonPath {
* @param filters filters to be applied to the filter place holders [?] in the path
* @return compiled JsonPath
*/
public static JsonPath compile(String jsonPath, Filter2... filters) {
public static JsonPath compile(String jsonPath, Filter... filters) {
notEmpty(jsonPath, "json can not be null or empty");
return new JsonPath(jsonPath, filters);
@ -395,7 +395,7 @@ public class JsonPath {
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static <T> T read(Object json, String jsonPath, Filter2... filters) {
public static <T> T read(Object json, String jsonPath, Filter... filters) {
//return compile(jsonPath, filters).read(json);
return new JsonReader().parse(json).read(jsonPath, filters);
}
@ -411,7 +411,7 @@ public class JsonPath {
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static <T> T read(String json, String jsonPath, Filter2... filters) {
public static <T> T read(String json, String jsonPath, Filter... filters) {
return new JsonReader().parse(json).read(jsonPath, filters);
}
@ -425,7 +425,7 @@ public class JsonPath {
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static <T> T read(URL jsonURL, String jsonPath, Filter2... filters) throws IOException {
public static <T> T read(URL jsonURL, String jsonPath, Filter... filters) throws IOException {
return new JsonReader().parse(jsonURL).read(jsonPath, filters);
}
@ -439,7 +439,7 @@ public class JsonPath {
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static <T> T read(File jsonFile, String jsonPath, Filter2... filters) throws IOException {
public static <T> T read(File jsonFile, String jsonPath, Filter... filters) throws IOException {
return new JsonReader().parse(jsonFile).read(jsonPath, filters);
}
@ -453,7 +453,7 @@ public class JsonPath {
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static <T> T read(InputStream jsonInputStream, String jsonPath, Filter2... filters) throws IOException {
public static <T> T read(InputStream jsonInputStream, String jsonPath, Filter... filters) throws IOException {
return new JsonReader().parse(jsonInputStream).read(jsonPath, filters);
}

4
json-path/src/main/java/com/jayway/jsonpath/ReadContext.java

@ -38,7 +38,7 @@ public interface ReadContext {
* @param <T>
* @return result
*/
<T> T read(String path, Filter2... filters);
<T> T read(String path, Filter... filters);
/**
* Reads the given path from this context
@ -56,7 +56,7 @@ public interface ReadContext {
* @param filters filters
* @return list of definite path strings to object matched by path
*/
List<String> readPathList(String path, Filter2... filters);
List<String> readPathList(String path, Filter... filters);
/**
* Reads the given path list from this context

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

@ -89,7 +89,7 @@ public class JsonReader implements ParseContext, ReadContext {
}
@Override
public <T> T read(String path, Filter2... filters) {
public <T> T read(String path, Filter... filters) {
notEmpty(path, "path can not be null or empty");
return read(JsonPath.compile(path, filters));
}
@ -101,7 +101,7 @@ public class JsonReader implements ParseContext, ReadContext {
}
@Override
public List<String> readPathList(String path, Filter2... filters) {
public List<String> readPathList(String path, Filter... filters) {
notEmpty(path, "path can not be null or empty");
return readPathList(JsonPath.compile(path, filters));
}

9
json-path/src/main/java/com/jayway/jsonpath/internal/spi/compiler/FilterPathToken.java

@ -2,7 +2,6 @@ package com.jayway.jsonpath.internal.spi.compiler;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.Filter2;
import com.jayway.jsonpath.InvalidPathException;
import java.util.Collection;
@ -23,13 +22,13 @@ class FilterPathToken extends PathToken {
"[?,?,?,?,?]"
};
private final Collection<Filter2> filters;
private final Collection<Filter> filters;
public FilterPathToken(Filter2 filter) {
public FilterPathToken(Filter filter) {
this.filters = asList(filter);
}
public FilterPathToken(Collection<Filter2> filters) {
public FilterPathToken(Collection<Filter> filters) {
this.filters = filters;
}
@ -52,7 +51,7 @@ class FilterPathToken extends PathToken {
public boolean accept(Object obj, Configuration configuration) {
boolean accept = true;
for (Filter2 filter : filters) {
for (Filter filter : filters) {
if (!filter.apply (obj, configuration)) {
accept = false;
break;

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

@ -1,9 +1,7 @@
package com.jayway.jsonpath.internal.spi.compiler;
import com.jayway.jsonpath.Criteria;
import com.jayway.jsonpath.Criteria2;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.Filter2;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.Cache;
import com.jayway.jsonpath.internal.Utils;
@ -12,7 +10,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
@ -35,11 +32,11 @@ public class PathCompiler {
public static Path tokenize(String path, Filter2... filters) {
public static Path tokenize(String path, Filter... filters) {
notEmpty(path, "Path may not be null empty");
path = path.trim();
LinkedList<Filter2> filterList = new LinkedList<Filter2>(asList(filters));
LinkedList<Filter> filterList = new LinkedList<Filter>(asList(filters));
if (!path.startsWith("$")) {
path = "$." + path;
@ -183,15 +180,15 @@ public class PathCompiler {
private int i;
private char current;
private final LinkedList<Filter2> filterList;
private final LinkedList<Filter> filterList;
private final String pathFragment;
PathComponentAnalyzer(String pathFragment, LinkedList<Filter2> filterList) {
PathComponentAnalyzer(String pathFragment, LinkedList<Filter> filterList) {
this.pathFragment = pathFragment;
this.filterList = filterList;
}
static PathToken analyze(String pathFragment, LinkedList<Filter2> filterList) {
static PathToken analyze(String pathFragment, LinkedList<Filter> filterList) {
return new PathComponentAnalyzer(pathFragment, filterList).analyze();
}
@ -205,7 +202,7 @@ public class PathCompiler {
else if (FILTER_PATTERN.matcher(pathFragment).matches()) {
final int criteriaCount = Utils.countMatches(pathFragment, "?");
List<Filter2> filters = new ArrayList<Filter2>(criteriaCount);
List<Filter> filters = new ArrayList<Filter>(criteriaCount);
for (int i = 0; i < criteriaCount; i++) {
filters.add(filterList.poll());
}
@ -245,7 +242,7 @@ public class PathCompiler {
StringBuilder pathBuffer = new StringBuilder();
StringBuilder operatorBuffer = new StringBuilder();
StringBuilder valueBuffer = new StringBuilder();
List<Criteria2> criteria = new ArrayList<Criteria2>();
List<Criteria> criteria = new ArrayList<Criteria>();
int bracketCount = 0;
@ -312,13 +309,13 @@ public class PathCompiler {
criteria.add(createCriteria(pathBuffer, operatorBuffer, valueBuffer));
Filter2 filter2 = Filter2.filter(criteria);
Filter filter2 = Filter.filter(criteria);
return new FilterPathToken(filter2);
}
private Criteria2 createCriteria(StringBuilder pathBuffer, StringBuilder operatorBuffer, StringBuilder valueBuffer) {
return Criteria2.create(pathBuffer.toString().trim(), operatorBuffer.toString().trim(), valueBuffer.toString().trim());
private Criteria createCriteria(StringBuilder pathBuffer, StringBuilder operatorBuffer, StringBuilder valueBuffer) {
return Criteria.create(pathBuffer.toString().trim(), operatorBuffer.toString().trim(), valueBuffer.toString().trim());
}
private boolean isAnd(char c) {

41
json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java

@ -75,4 +75,45 @@ public class ComplianceTest {
assertThat(JsonPath.<String>read(json, "$.points[(@.length - 1)].id"), equalTo("i6"));
}
@Test
public void test_four() throws Exception {
String json = "{ \"menu\": {\n" +
" \"header\": \"SVG Viewer\",\n" +
" \"items\": [\n" +
" {\"id\": \"Open\"},\n" +
" {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n" +
" null,\n" +
" {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n" +
" {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n" +
" {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n" +
" null,\n" +
" {\"id\": \"Quality\"},\n" +
" {\"id\": \"Pause\"},\n" +
" {\"id\": \"Mute\"},\n" +
" null,\n" +
" {\"id\": \"Find\", \"label\": \"Find...\"},\n" +
" {\"id\": \"FindAgain\", \"label\": \"Find Again\"},\n" +
" {\"id\": \"Copy\"},\n" +
" {\"id\": \"CopyAgain\", \"label\": \"Copy Again\"},\n" +
" {\"id\": \"CopySVG\", \"label\": \"Copy SVG\"},\n" +
" {\"id\": \"ViewSVG\", \"label\": \"View SVG\"},\n" +
" {\"id\": \"ViewSource\", \"label\": \"View Source\"},\n" +
" {\"id\": \"SaveAs\", \"label\": \"Save As\"},\n" +
" null,\n" +
" {\"id\": \"Help\"},\n" +
" {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n" +
" ]\n" +
" }\n" +
" }";
//assertThat(JsonPath.<List<String>>read(json, "$.menu.items[?(@)]"), Matchers.is(notNullValue()));
assertThat(JsonPath.<List<String>>read(json, "$.menu.items[?(@ && @.id == 'ViewSVG')].id"), hasItems("ViewSVG"));
//assertThat(JsonPath.<List<String>>read(json, "$.menu.items[?(@ && @.id && !@.label)].id"), hasItems("?")); //low
//assertThat(JsonPath.<List<String>>read(json, "$.menu.items[?(@ && @.label && /SVG/.test(@.label))].id"), hasItems("?")); //low
//assertThat(JsonPath.<List<String>>read(json, "$.menu.items[?(!@)]"), hasItems("?")); //low
//assertThat(JsonPath.<List<String>>read(json, "$..[0]"), hasItems("?")); //low
}
}

4
json-path/src/test/java/com/jayway/jsonpath/Filter2Test.java

@ -6,8 +6,8 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria2.*;
import static com.jayway.jsonpath.Filter2.filter;
import static com.jayway.jsonpath.Criteria.*;
import static com.jayway.jsonpath.Filter.filter;
import static org.assertj.core.api.Assertions.assertThat;
public class Filter2Test {

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

@ -10,8 +10,8 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria2.where;
import static com.jayway.jsonpath.Filter2.filter;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
import static java.util.Arrays.asList;
import static junit.framework.Assert.*;
import static org.hamcrest.CoreMatchers.is;
@ -269,7 +269,7 @@ public class FilterTest {
"]";
Filter2 filter = filter(
Filter filter = filter(
where("first-name").is("Jock")
.and("address.state").is("Texas"));
@ -307,8 +307,8 @@ public class FilterTest {
check.put("long", 1L);
check.put("double", 1.12D);
Filter2 shouldMarch = filter(where("string").is("foo").and("int").lt(11));
Filter2 shouldNotMarch = filter(where("string").is("foo").and("int").gt(11));
Filter shouldMarch = filter(where("string").is("foo").and("int").lt(11));
Filter shouldNotMarch = filter(where("string").is("foo").and("int").gt(11));
assertTrue(shouldMarch.apply(check, conf));
assertFalse(shouldNotMarch.apply(check, conf));
@ -324,7 +324,7 @@ public class FilterTest {
check.put("long", 1L);
check.put("double", 1.12D);
Filter2 filter = filter(where("string").is("foo").and("int").lt(11));
Filter filter = filter(where("string").is("foo").and("int").lt(11));
assertTrue(filter.apply(check, conf));
@ -344,11 +344,11 @@ public class FilterTest {
check.put("long", 1L);
check.put("double", 1.12D);
Filter2 filter = filter(where("string").is("foo"));
Filter filter = filter(where("string").is("foo"));
assertTrue(filter.apply(check, conf));
Criteria2 criteria = where("string").is("not eq");
Criteria criteria = where("string").is("not eq");
filter.addCriteria(criteria);

2
json-path/src/test/java/com/jayway/jsonpath/IssuesTest.java

@ -260,7 +260,7 @@ public class IssuesTest {
@Test
public void issue_29_b() throws Exception {
String json = "{\"list\": [ { \"a\":\"atext\", \"b\":{ \"b-a\":\"batext\", \"b-b\":\"bbtext\" } }, { \"a\":\"atext2\", \"b\":{ \"b-a\":\"batext2\", \"b-b\":\"bbtext2\" } } ] }";
List<String> result = JsonPath.read(json, "$.list[?]", Filter2.filter(Criteria2.where("b.b-a").eq("batext2")));
List<String> result = JsonPath.read(json, "$.list[?]", Filter.filter(Criteria.where("b.b-a").eq("batext2")));
assertTrue(result.size() == 1);
}

Loading…
Cancel
Save