Browse Source

Improved Filter and Criteria.

pull/8/merge
Kalle Stenflo 13 years ago
parent
commit
89b74987ba
  1. 354
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  2. 26
      json-path/src/main/java/com/jayway/jsonpath/Filter.java
  3. 7
      json-path/src/main/java/com/jayway/jsonpath/JsonModel.java
  4. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilter.java
  5. 1
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayQueryFilter.java
  6. 21
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterFactory.java
  7. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/PathTokenFilter.java
  8. 24
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/eval/ExpressionEvaluator.java
  9. 33
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/AbstractJsonProvider.java
  10. 1
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/JacksonProvider.java
  11. 4
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/JsonSmartJsonProvider.java
  12. 44
      json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
  13. 14
      pom.xml

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

@ -32,23 +32,39 @@ public class Criteria {
*/ */
private static final Object NOT_SET = new Object(); private static final Object NOT_SET = new Object();
private String key; private enum CriteriaType {
GT,
GTE,
LT,
LTE,
NE,
IN,
NIN,
ALL,
SIZE,
EXISTS,
TYPE,
REGEX,
OR
}
private final String key;
private List<Criteria> criteriaChain; private final List<Criteria> criteriaChain;
private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>(); private final LinkedHashMap<CriteriaType, Object> criteria = new LinkedHashMap<CriteriaType, Object>();
private Object isValue = NOT_SET; private Object isValue = NOT_SET;
public Criteria(String key) { private Criteria(String key) {
notEmpty(key, "key can not be null or empty"); notEmpty(key, "key can not be null or empty");
this.criteriaChain = new ArrayList<Criteria>(); this.criteriaChain = new ArrayList<Criteria>();
this.criteriaChain.add(this); this.criteriaChain.add(this);
this.key = key; this.key = key;
} }
protected Criteria(List<Criteria> criteriaChain, String key) { private Criteria(List<Criteria> criteriaChain, String key) {
notEmpty(key, "key can not be null or empty"); notEmpty(key, "key can not be null or empty");
this.criteriaChain = criteriaChain; this.criteriaChain = criteriaChain;
this.criteriaChain.add(this); this.criteriaChain.add(this);
@ -59,14 +75,20 @@ public class Criteria {
return this.key; return this.key;
} }
public boolean apply(Map<String, Object> map) { /**
* Checks if this criteria matches the given map
*
* @param map map to check
* @return true if criteria is a match
*/
public boolean matches(Map<String, Object> map) {
if (this.criteriaChain.size() == 1) { if (this.criteriaChain.size() == 1) {
return criteriaChain.get(0).singleObjectApply(map); return criteriaChain.get(0).singleObjectApply(map);
} else { } else {
for (Criteria c : this.criteriaChain) { for (Criteria c : this.criteriaChain) {
System.out.println("");
if (!c.singleObjectApply(map)) { if (!c.singleObjectApply(map)) {
return false; return false;
} }
@ -75,150 +97,152 @@ public class Criteria {
} }
} }
protected boolean singleObjectApply(Map<String, Object> map) {
boolean not = false;
for (String key : this.criteria.keySet()) {
Object expectedVal = null;
Object actualVal = map.get(this.key);
if (not) {
expectedVal = this.criteria.get(key);
not = false;
} else {
if ("$not".equals(key)) {
not = true;
} else {
expectedVal = this.criteria.get(key);
if ("$gt".equals(key)) {
if (expectedVal == null || actualVal == null) {
return false;
}
Number expectedNumber = (Number) expectedVal;
Number actualNumber = (Number) actualVal;
return (actualNumber.doubleValue() > expectedNumber.doubleValue()); boolean singleObjectApply(Map<String, Object> map) {
} else if ("$gte".equals(key)) { for (CriteriaType key : this.criteria.keySet()) {
if (expectedVal == null || actualVal == null) { Object actualVal = map.get(this.key);
return false; Object expectedVal = this.criteria.get(key);
}
Number expectedNumber = (Number) expectedVal; if (CriteriaType.GT.equals(key)) {
Number actualNumber = (Number) actualVal;
return (actualNumber.doubleValue() >= expectedNumber.doubleValue()); if (expectedVal == null || actualVal == null) {
return false;
}
} else if ("$lt".equals(key)) { Number expectedNumber = (Number) expectedVal;
Number actualNumber = (Number) actualVal;
if (expectedVal == null || actualVal == null) { return (actualNumber.doubleValue() > expectedNumber.doubleValue());
return false;
}
Number expectedNumber = (Number) expectedVal;
Number actualNumber = (Number) actualVal;
return (actualNumber.doubleValue() < expectedNumber.doubleValue()); } else if (CriteriaType.GTE.equals(key)) {
} else if ("$lte".equals(key)) { if (expectedVal == null || actualVal == null) {
return false;
}
if (expectedVal == null || actualVal == null) { Number expectedNumber = (Number) expectedVal;
return false; Number actualNumber = (Number) actualVal;
}
Number expectedNumber = (Number) expectedVal; return (actualNumber.doubleValue() >= expectedNumber.doubleValue());
Number actualNumber = (Number) actualVal;
return (actualNumber.doubleValue() <= expectedNumber.doubleValue()); } else if (CriteriaType.LT.equals(key)) {
} else if ("$ne".equals(key)) { if (expectedVal == null || actualVal == null) {
if (expectedVal == null && actualVal == null) { return false;
return false; }
}
if (expectedVal == null) {
return !(actualVal == null);
}
return !expectedVal.equals(actualVal); Number expectedNumber = (Number) expectedVal;
Number actualNumber = (Number) actualVal;
} else if ("$in".equals(key)) { return (actualNumber.doubleValue() < expectedNumber.doubleValue());
Collection exp = (Collection) expectedVal; } else if (CriteriaType.LTE.equals(key)) {
return exp.contains(actualVal); if (expectedVal == null || actualVal == null) {
return false;
}
} else if ("$nin".equals(key)) { Number expectedNumber = (Number) expectedVal;
Number actualNumber = (Number) actualVal;
Collection exp = (Collection) expectedVal; return (actualNumber.doubleValue() <= expectedNumber.doubleValue());
return !exp.contains(actualVal); } else if (CriteriaType.NE.equals(key)) {
} else if ("$all".equals(key)) { if (expectedVal == null && actualVal == null) {
return false;
}
if (expectedVal == null) {
return true;
} else {
return !expectedVal.equals(actualVal);
}
Collection exp = (Collection) expectedVal; } else if (CriteriaType.IN.equals(key)) {
Collection act = (Collection) actualVal;
return act.containsAll(exp); Collection exp = (Collection) expectedVal;
} else if ("$size".equals(key)) { return exp.contains(actualVal);
int exp = (Integer) expectedVal; } else if (CriteriaType.NIN.equals(key)) {
List act = (List) actualVal;
return (act.size() == exp); Collection exp = (Collection) expectedVal;
} else if ("$exists".equals(key)) { return !exp.contains(actualVal);
} else if (CriteriaType.ALL.equals(key)) {
boolean exp = (Boolean) expectedVal; Collection exp = (Collection) expectedVal;
boolean act = map.containsKey(this.key); Collection act = (Collection) actualVal;
return act == exp; return act.containsAll(exp);
} else if ("$type".equals(key)) { } else if (CriteriaType.SIZE.equals(key)) {
Class<?> exp = (Class<?>) expectedVal; int exp = (Integer) expectedVal;
Class<?> act = null; List act = (List) actualVal;
if (map.containsKey(this.key)) {
Object actVal = map.get(this.key);
if (actVal != null) {
act = actVal.getClass();
}
}
if (act == null) {
return false;
} else {
return act.equals(exp);
}
} else if ("$regex".equals(key)) { return (act.size() == exp);
} else if (CriteriaType.EXISTS.equals(key)) {
Pattern exp = (Pattern) expectedVal; boolean exp = (Boolean) expectedVal;
String act = (String) actualVal; boolean act = map.containsKey(this.key);
if(act == null){
return false;
}
return exp.matcher(act).matches();
} else if ("$or".equals(key)) { return act == exp;
} else if (CriteriaType.TYPE.equals(key)) {
throw new UnsupportedOperationException("Or not supported yet"); Class<?> exp = (Class<?>) expectedVal;
Class<?> act = null;
if (map.containsKey(this.key)) {
Object actVal = map.get(this.key);
if (actVal != null) {
act = actVal.getClass();
} }
}
if (act == null) {
return false;
} else {
return act.equals(exp);
}
} else if (CriteriaType.REGEX.equals(key)) {
}
Pattern exp = (Pattern) expectedVal;
String act = (String) actualVal;
if (act == null) {
return false;
}
return exp.matcher(act).matches();
} else {
throw new UnsupportedOperationException("Criteria type not supported: " + key.name());
} }
} }
if (isValue != NOT_SET) { if (isValue != NOT_SET) {
if (isValue == null) { if (isValue instanceof Collection) {
return (map.get(key) == null); Collection<Criteria> cs = (Collection<Criteria>) isValue;
for (Criteria crit : cs) {
for (Criteria c : crit.criteriaChain) {
if (!c.singleObjectApply(map)) {
return false;
}
}
}
return true;
} else { } else {
return isValue.equals(map.get(key));
if (isValue == null) {
return (map.get(key) == null);
} else {
return isValue.equals(map.get(key));
}
} }
} else { } else {
@ -230,8 +254,8 @@ public class Criteria {
/** /**
* Static factory method to create a Criteria using the provided key * Static factory method to create a Criteria using the provided key
* *
* @param key * @param key filed name
* @return * @return the new criteria
*/ */
public static Criteria where(String key) { public static Criteria where(String key) {
@ -241,7 +265,8 @@ public class Criteria {
/** /**
* Static factory method to create a Criteria using the provided key * Static factory method to create a Criteria using the provided key
* *
* @return * @param key ads new filed to criteria
* @return the criteria builder
*/ */
public Criteria and(String key) { public Criteria and(String key) {
return new Criteria(this.criteriaChain, key); return new Criteria(this.criteriaChain, key);
@ -264,6 +289,7 @@ public class Criteria {
this.isValue = o; this.isValue = o;
return this; return this;
} }
/** /**
* Creates a criterion using equality * Creates a criterion using equality
* *
@ -275,62 +301,63 @@ public class Criteria {
} }
/** /**
* Creates a criterion using the $ne operator * Creates a criterion using the <b>!=</b> operator
* *
* @param o * @param o
* @return * @return
*/ */
public Criteria ne(Object o) { public Criteria ne(Object o) {
criteria.put("$ne", o); criteria.put(CriteriaType.NE, o);
return this; return this;
} }
/** /**
* Creates a criterion using the $lt operator * Creates a criterion using the <b>&lt;</b> operator
* *
* @param o * @param o
* @return * @return
*/ */
public Criteria lt(Object o) { public Criteria lt(Object o) {
criteria.put("$lt", o); criteria.put(CriteriaType.LT, o);
return this; return this;
} }
/** /**
* Creates a criterion using the $lte operator * Creates a criterion using the <b>&lt;=</b> operator
* *
* @param o * @param o
* @return * @return
*/ */
public Criteria lte(Object o) { public Criteria lte(Object o) {
criteria.put("$lte", o); criteria.put(CriteriaType.LTE, o);
return this; return this;
} }
/** /**
* Creates a criterion using the $gt operator * Creates a criterion using the <b>&gt;</b> operator
* *
* @param o * @param o
* @return * @return
*/ */
public Criteria gt(Object o) { public Criteria gt(Object o) {
criteria.put("$gt", o); criteria.put(CriteriaType.GT, o);
return this; return this;
} }
/** /**
* Creates a criterion using the $gte operator * Creates a criterion using the <b>&gt;=</b> operator
* *
* @param o * @param o
* @return * @return
*/ */
public Criteria gte(Object o) { public Criteria gte(Object o) {
criteria.put("$gte", o); criteria.put(CriteriaType.GTE, o);
return this; return this;
} }
/** /**
* Creates a criterion using the $in operator * 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 * @param o the values to match against
* @return * @return
@ -344,35 +371,45 @@ public class Criteria {
} }
/** /**
* Creates a criterion using the $in operator * 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 * @param c the collection containing the values to match against
*/ * @return
*/
public Criteria in(Collection<?> c) { public Criteria in(Collection<?> c) {
criteria.put("$in", c); notNull(c, "collection can not be null");
criteria.put(CriteriaType.IN, c);
return this; return this;
} }
/** /**
* Creates a criterion using the $nin operator * 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 * @param o the values to match against
* @return * @return
*/ */
public Criteria nin(Object... o) { public Criteria nin(Object... o) {
return nin(Arrays.asList(o)); return nin(Arrays.asList(o));
} }
public Criteria nin(Collection<?> o) { /**
notNull(o, "collection can not be null"); * The <code>nin</code> operator is similar to $in except that it selects objects for
criteria.put("$nin", o); * which the specified field does not have any value in the specified array.
*
* @param c the values to match against
* @return
*/
public Criteria nin(Collection<?> c) {
notNull(c, "collection can not be null");
criteria.put(CriteriaType.NIN, c);
return this; return this;
} }
/** /**
* Creates a criterion using the $all operator * 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 * @param o
* @return * @return
@ -380,94 +417,77 @@ public class Criteria {
public Criteria all(Object... o) { public Criteria all(Object... o) {
return all(Arrays.asList(o)); return all(Arrays.asList(o));
} }
/**
public Criteria all(Collection<?> 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.
criteria.put("$all", o); *
* @param c
* @return
*/
public Criteria all(Collection<?> c) {
notNull(c, "collection can not be null");
criteria.put(CriteriaType.ALL, c);
return this; return this;
} }
/** /**
* Creates a criterion using the $size operator * The <code>size</code> operator matches any array with the specified number of elements.
* *
* @param s * @param s
* @return * @return
*/ */
public Criteria size(int s) { public Criteria size(int s) {
criteria.put("$size", s); criteria.put(CriteriaType.SIZE, s);
return this; return this;
} }
/** /**
* Creates a criterion using the $exists operator * Check for existence (or lack thereof) of a field.
* *
* @param b * @param b
* @return * @return
*/ */
public Criteria exists(boolean b) { public Criteria exists(boolean b) {
criteria.put("$exists", b); criteria.put(CriteriaType.EXISTS, b);
return this; return this;
} }
/** /**
* Creates a criterion using the $type operator * The $type operator matches values based on their Java type.
* *
* @param t * @param t
* @return * @return
*/ */
public Criteria type(Class<?> t) { public Criteria type(Class<?> t) {
criteria.put("$type", t); notNull(t, "type can not be null");
return this; criteria.put(CriteriaType.TYPE, t);
}
/**
* Creates a criterion using the $not meta operator which affects the clause directly following
*
* @return
*/
public Criteria not() {
criteria.put("$not", null);
return this; return this;
} }
/** /**
* Creates a criterion using a $regex and $options * Creates a criterion using a Regex
* *
* @param pattern * @param pattern
* @return * @return
*/ */
public Criteria regex(Pattern pattern) { public Criteria regex(Pattern pattern) {
criteria.put("$regex", pattern); notNull(pattern, "pattern can not be null");
criteria.put(CriteriaType.REGEX, pattern);
return this; return this;
} }
/**
* Creates a criterion using the $mod operator
*
* @param value
* @param remainder
* @return
*/
/*
public Criteria mod(Number value, Number remainder) {
List<Object> l = new ArrayList<Object>();
l.add(value);
l.add(remainder);
criteria.put("$mod", l);
return this;
}
*/
/** /**
* Creates an 'or' criteria using the $or operator for all of the provided criteria * Creates an 'or' criteria using the $or operator for all of the provided criteria
* *
* @param criteria * @param criteria
*/ */
/*
public Criteria orOperator(Criteria... criteria) { public Criteria orOperator(Criteria... criteria) {
criteriaChain.add(new Criteria("$or").is(asList(criteria))); criteriaChain.add(new Criteria("$or").is(asList(criteria)));
return this; return this;
} }
*/
/** /**
* Creates a 'nor' criteria using the $nor operator for all of the provided criteria * Creates a 'nor' criteria using the $nor operator for all of the provided criteria

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

@ -27,16 +27,26 @@ import java.util.*;
* List<String> names = JsonPath.read(doc, "$items[?].name", Filter.filter(Criteria.where("name").is("john")); * List<String> names = JsonPath.read(doc, "$items[?].name", Filter.filter(Criteria.where("name").is("john"));
* </code> * </code>
* *
* @See Criteria * @see Criteria
* *
* @author Kalle Stenflo * @author Kalle Stenflo
*/ */
public abstract class Filter<T> { public abstract class Filter<T> {
/**
* Creates a new filter based on given criteria
* @param criteria the filter criteria
* @return a new filter
*/
public static Filter filter(Criteria criteria) { public static Filter filter(Criteria criteria) {
return new MapFilter(criteria); return new MapFilter(criteria);
} }
/**
* Filters the provided list based on this filter configuration
* @param filterItems items to filter
* @return the filtered list
*/
public List<T> doFilter(List<T> filterItems) { public List<T> doFilter(List<T> filterItems) {
List<T> result = new ArrayList<T>(); List<T> result = new ArrayList<T>();
for (T filterItem : filterItems) { for (T filterItem : filterItems) {
@ -47,9 +57,19 @@ public abstract class Filter<T> {
return result; return result;
} }
/**
* 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); public abstract boolean accept(T obj);
/**
* Adds a new criteria to this filter
*
* @param criteria to add
* @return the updated filter
*/
public abstract Filter addCriteria(Criteria criteria); public abstract Filter addCriteria(Criteria criteria);
@ -94,7 +114,7 @@ public abstract class Filter<T> {
@Override @Override
public boolean accept(Map<String, Object> map) { public boolean accept(Map<String, Object> map) {
for (Criteria criterion : this.criteria.values()) { for (Criteria criterion : this.criteria.values()) {
if (!criterion.apply(map)) { if (!criterion.matches(map)) {
return false; return false;
} }
} }

7
json-path/src/main/java/com/jayway/jsonpath/JsonModel.java

@ -58,6 +58,7 @@ public class JsonModel {
* Note that the jsonObject must either a {@link List} or a {@link Map} * Note that the jsonObject must either a {@link List} or a {@link Map}
* *
* @param jsonObject the json object * @param jsonObject the json object
* @param jsonProvider
*/ */
private JsonModel(Object jsonObject, JsonProvider jsonProvider) { private JsonModel(Object jsonObject, JsonProvider jsonProvider) {
notNull(jsonObject, "json can not be null"); notNull(jsonObject, "json can not be null");
@ -73,6 +74,7 @@ public class JsonModel {
* Creates a new JsonModel based on an {@link InputStream} * Creates a new JsonModel based on an {@link InputStream}
* *
* @param jsonInputStream the input stream * @param jsonInputStream the input stream
* @param jsonProvider
*/ */
private JsonModel(InputStream jsonInputStream, JsonProvider jsonProvider) { private JsonModel(InputStream jsonInputStream, JsonProvider jsonProvider) {
notNull(jsonInputStream, "jsonInputStream can not be null"); notNull(jsonInputStream, "jsonInputStream can not be null");
@ -84,7 +86,8 @@ public class JsonModel {
* Creates a new JsonModel by fetching the content from the provided URL * Creates a new JsonModel by fetching the content from the provided URL
* *
* @param jsonURL the URL to read * @param jsonURL the URL to read
* @throws IOException * @param jsonProvider
* @throws IOException failed to load URL
*/ */
private JsonModel(URL jsonURL, JsonProvider jsonProvider) throws IOException { private JsonModel(URL jsonURL, JsonProvider jsonProvider) throws IOException {
notNull(jsonURL, "jsonURL can not be null"); notNull(jsonURL, "jsonURL can not be null");
@ -266,7 +269,7 @@ public class JsonModel {
} while (!tokens.isEmpty()); } while (!tokens.isEmpty());
if (modelRef.getClass().isAssignableFrom(clazz)) { if (modelRef.getClass().isAssignableFrom(clazz)) {
throw new InvalidModelPathException(jsonPath + " does nor refer to a Map but " + (currentToken != null ? currentToken.getClass().getName() : "null")); throw new InvalidModelPathException(jsonPath + " does nor refer to a Map but " + currentToken.getClass().getName());
} }
return clazz.cast(modelRef); return clazz.cast(modelRef);
} }

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

@ -102,8 +102,8 @@ public class ArrayEvalFilter extends PathTokenFilter {
} }
private class ConditionStatement { private class ConditionStatement {
private String field; private final String field;
private String operator; private final String operator;
private String expected; private String expected;
private ConditionStatement(String field, String operator, String expected) { private ConditionStatement(String field, String operator, String expected) {

1
json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayQueryFilter.java

@ -19,7 +19,6 @@ import com.jayway.jsonpath.spi.JsonProvider;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* @author Kalle Stenflo * @author Kalle Stenflo

21
json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterFactory.java

@ -23,14 +23,15 @@ public class FilterFactory {
private final static PathTokenFilter ALL_ARRAY_ITEMS_FILTER = new PassthroughFilter("[*]", true); private final static PathTokenFilter ALL_ARRAY_ITEMS_FILTER = new PassthroughFilter("[*]", true);
private final static PathTokenFilter WILDCARD_FILTER = new WildcardFilter("*"); private final static PathTokenFilter WILDCARD_FILTER = new WildcardFilter("*");
private final static PathTokenFilter SCAN_FILTER = new ScanFilter(".."); private final static PathTokenFilter SCAN_FILTER = new ScanFilter("..");
private final static PathTokenFilter ARRAY_QUERY_FILTER = new ArrayQueryFilter("[?]");
public static PathTokenFilter createFilter(String pathFragment) { public static PathTokenFilter createFilter(String pathFragment) {
if ("$".equals(pathFragment)) { if (DOCUMENT_FILTER.getCondition().equals(pathFragment)) { //"$"
return DOCUMENT_FILTER; return DOCUMENT_FILTER;
} else if("[*]".equals(pathFragment)){ } else if (ALL_ARRAY_ITEMS_FILTER.getCondition().equals(pathFragment)) { //"[*]"
return ALL_ARRAY_ITEMS_FILTER; return ALL_ARRAY_ITEMS_FILTER;
@ -38,22 +39,23 @@ public class FilterFactory {
return WILDCARD_FILTER; return WILDCARD_FILTER;
} else if (pathFragment.contains("..")) { //} else if (pathFragment.contains("..")) {
} else if (SCAN_FILTER.getCondition().equals(pathFragment)) {
return SCAN_FILTER; return SCAN_FILTER;
} else if (!pathFragment.contains("[")) { } else if (ARRAY_QUERY_FILTER.getCondition().equals(pathFragment)) { //"[?]"
return new FieldFilter(pathFragment); return ARRAY_QUERY_FILTER;
} else if (pathFragment.equals("[?]")) { } else if (!pathFragment.contains("[")) {
return new ArrayQueryFilter(pathFragment); return new FieldFilter(pathFragment);
} else if (pathFragment.contains("[")) { } else if (pathFragment.contains("[")) {
if (pathFragment.startsWith("[?")) { if (pathFragment.startsWith("[?")) {
if(!pathFragment.contains("=") && !pathFragment.contains("<") && !pathFragment.contains(">")){ if (!pathFragment.contains("=") && !pathFragment.contains("<") && !pathFragment.contains(">")) {
//[?(@.isbn)] //[?(@.isbn)]
return new HasFieldFilter(pathFragment); return new HasFieldFilter(pathFragment);
} else { } else {
@ -69,10 +71,9 @@ public class FilterFactory {
} }
} }
throw new UnsupportedOperationException(".."); throw new UnsupportedOperationException("can not find filter for path fragment " + pathFragment);
} }
} }

3
json-path/src/main/java/com/jayway/jsonpath/internal/filter/PathTokenFilter.java

@ -30,6 +30,9 @@ public abstract class PathTokenFilter {
this.condition = condition; this.condition = condition;
} }
String getCondition() {
return condition;
}
String trim(String str, int front, int end) { String trim(String str, int front, int end) {
String res = str; String res = str;

24
json-path/src/main/java/com/jayway/jsonpath/internal/filter/eval/ExpressionEvaluator.java

@ -31,13 +31,13 @@ public class ExpressionEvaluator {
} else if ("!=".equals(comparator) || "<>".equals(comparator)) { } else if ("!=".equals(comparator) || "<>".equals(comparator)) {
return a.longValue() != e.longValue(); return a.longValue() != e.longValue();
} else if (">".equals(comparator)) { } else if (">".equals(comparator)) {
return a.longValue() > e.longValue(); return a > e;
} else if (">=".equals(comparator)) { } else if (">=".equals(comparator)) {
return a.longValue() >= e.longValue(); return a >= e;
} else if ("<".equals(comparator)) { } else if ("<".equals(comparator)) {
return a.longValue() < e.longValue(); return a < e;
} else if ("<=".equals(comparator)) { } else if ("<=".equals(comparator)) {
return a.longValue() <= e.longValue(); return a <= e;
} }
} else if (actual instanceof Integer) { } else if (actual instanceof Integer) {
Integer a = (Integer) actual; Integer a = (Integer) actual;
@ -48,13 +48,13 @@ public class ExpressionEvaluator {
} else if ("!=".equals(comparator) || "<>".equals(comparator)) { } else if ("!=".equals(comparator) || "<>".equals(comparator)) {
return a.intValue() != e.intValue(); return a.intValue() != e.intValue();
} else if (">".equals(comparator)) { } else if (">".equals(comparator)) {
return a.intValue() > e.intValue(); return a > e;
} else if (">=".equals(comparator)) { } else if (">=".equals(comparator)) {
return a.intValue() >= e.intValue(); return a >= e;
} else if ("<".equals(comparator)) { } else if ("<".equals(comparator)) {
return a.intValue() < e.intValue(); return a < e;
} else if ("<=".equals(comparator)) { } else if ("<=".equals(comparator)) {
return a.intValue() <= e.intValue(); return a <= e;
} }
} else if (actual instanceof Double) { } else if (actual instanceof Double) {
@ -66,13 +66,13 @@ public class ExpressionEvaluator {
} else if ("!=".equals(comparator) || "<>".equals(comparator)) { } else if ("!=".equals(comparator) || "<>".equals(comparator)) {
return a.doubleValue() != e.doubleValue(); return a.doubleValue() != e.doubleValue();
} else if (">".equals(comparator)) { } else if (">".equals(comparator)) {
return a.doubleValue() > e.doubleValue(); return a > e;
} else if (">=".equals(comparator)) { } else if (">=".equals(comparator)) {
return a.doubleValue() >= e.doubleValue(); return a >= e;
} else if ("<".equals(comparator)) { } else if ("<".equals(comparator)) {
return a.doubleValue() < e.doubleValue(); return a < e;
} else if ("<=".equals(comparator)) { } else if ("<=".equals(comparator)) {
return a.doubleValue() <= e.doubleValue(); return a <= e;
} }
} else if (actual instanceof String) { } else if (actual instanceof String) {

33
json-path/src/main/java/com/jayway/jsonpath/spi/impl/AbstractJsonProvider.java

@ -1,3 +1,17 @@
/*
* 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.spi.impl; package com.jayway.jsonpath.spi.impl;
import com.jayway.jsonpath.spi.JsonProvider; import com.jayway.jsonpath.spi.JsonProvider;
@ -6,10 +20,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Created by IntelliJ IDEA. * @author Kalle Stenflo
* User: kallestenflo
* Date: 3/2/12
* Time: 9:56 PM
*/ */
public abstract class AbstractJsonProvider implements JsonProvider { public abstract class AbstractJsonProvider implements JsonProvider {
@ -36,8 +47,8 @@ public abstract class AbstractJsonProvider implements JsonProvider {
/** /**
* Converts give object to a List * Converts give object to a List
* *
* @param list * @param list object to convert
* @return * @return object as list
*/ */
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
public List<Object> toList(Object list) { public List<Object> toList(Object list) {
@ -48,8 +59,8 @@ public abstract class AbstractJsonProvider implements JsonProvider {
/** /**
* Converts given object to a Map * Converts given object to a Map
* *
* @param map * @param map object to convert
* @return * @return object as map
*/ */
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
public Map<String, Object> toMap(Object map) { public Map<String, Object> toMap(Object map) {
@ -59,9 +70,9 @@ public abstract class AbstractJsonProvider implements JsonProvider {
/** /**
* Extracts a value from a Map * Extracts a value from a Map
* *
* @param map * @param map map to read from
* @param key * @param key key to read
* @return * @return value of key in map
*/ */
public Object getMapValue(Object map, String key) { public Object getMapValue(Object map, String key) {
return toMap(map).get(key); return toMap(map).get(key);

1
json-path/src/main/java/com/jayway/jsonpath/spi/impl/JacksonProvider.java

@ -31,6 +31,7 @@ import java.util.*;
public class JacksonProvider extends AbstractJsonProvider implements MappingProvider{ public class JacksonProvider extends AbstractJsonProvider implements MappingProvider{
private ObjectMapper objectMapper = new ObjectMapper(); private ObjectMapper objectMapper = new ObjectMapper();
@Override @Override
public Mode getMode() { public Mode getMode() {
return Mode.STRICT; return Mode.STRICT;

4
json-path/src/main/java/com/jayway/jsonpath/spi/impl/JsonSmartJsonProvider.java

@ -83,9 +83,9 @@ public class JsonSmartJsonProvider extends AbstractJsonProvider {
public String toJson(Object obj) { public String toJson(Object obj) {
if(obj instanceof Map) { if(obj instanceof Map) {
return JSONObject.toJSONString((Map<String, ? extends Object>) obj); return JSONObject.toJSONString((Map<String, ?>) obj);
} else if(obj instanceof List){ } else if(obj instanceof List){
return JSONArray.toJSONString((List<? extends Object>) obj); return JSONArray.toJSONString((List<?>) obj);
} else { } else {
throw new UnsupportedOperationException(obj.getClass().getName() + " can not be converted to JSON"); throw new UnsupportedOperationException(obj.getClass().getName() + " can not be converted to JSON");
} }

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

@ -11,9 +11,7 @@ import java.util.regex.Pattern;
import static com.jayway.jsonpath.Criteria.where; import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter; import static com.jayway.jsonpath.Filter.filter;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.*;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
/** /**
* Created by IntelliJ IDEA. * Created by IntelliJ IDEA.
@ -131,10 +129,10 @@ public class FilterTest {
check.put("foo", 12.5D); check.put("foo", 12.5D);
check.put("foo_null", null); check.put("foo_null", null);
assertTrue(filter(where("foo").lt(13D)).accept(check)); assertTrue(filter(where("foo").lte(13D)).accept(check));
assertFalse(filter(where("foo").lt(null)).accept(check)); assertFalse(filter(where("foo").lte(null)).accept(check));
assertFalse(filter(where("foo").lt(5D)).accept(check)); assertFalse(filter(where("foo").lte(5D)).accept(check));
assertFalse(filter(where("foo_null").lt(5D)).accept(check)); assertFalse(filter(where("foo_null").lte(5D)).accept(check));
} }
@Test @Test
@ -267,7 +265,7 @@ public class FilterTest {
} }
@Test @Test
public void filters_can_be_extended() throws Exception { public void filters_can_be_extended_with_new_criteria() throws Exception {
Map<String, Object> check = new HashMap<String, Object>(); Map<String, Object> check = new HashMap<String, Object>();
check.put("string", "foo"); check.put("string", "foo");
@ -286,6 +284,36 @@ public class FilterTest {
} }
@Test
public void filters_criteria_can_be_refined() throws Exception {
Map<String, Object> check = new HashMap<String, Object>();
check.put("string", "foo");
check.put("string_null", null);
check.put("int", 10);
check.put("long", 1L);
check.put("double", 1.12D);
Filter filter = filter(where("string").is("foo"));
assertTrue(filter.accept(check));
Criteria criteria = where("string").is("not eq");
filter.addCriteria(criteria);
assertFalse(filter.accept(check));
filter = filter(where("string").is("foo").and("string").is("not eq"));
assertFalse(filter.accept(check));
filter = filter(where("string").is("foo").and("string").is("foo"));
assertTrue(filter.accept(check));
}
@Test @Test
public void arrays_of_maps_can_be_filtered() throws Exception { public void arrays_of_maps_can_be_filtered() throws Exception {

14
pom.xml

@ -216,6 +216,7 @@
<dependency> <dependency>
<groupId>org.codehaus.jackson</groupId> <groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId> <artifactId>jackson-mapper-asl</artifactId>
<optional>true</optional>
<version>1.9.5</version> <version>1.9.5</version>
</dependency> </dependency>
@ -243,6 +244,18 @@
<version>2.1</version> <version>2.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
@ -250,7 +263,6 @@
<version>4.10</version> <version>4.10</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
</project> </project>
Loading…
Cancel
Save