Browse Source

ArrayEvalFilter: add support for @.foo.

Delete now-unused HasPathFilter class.
pull/39/head
Laurent Charrière 11 years ago
parent
commit
9d87427970
  1. 95
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilter.java
  2. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterFactory.java
  3. 72
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/HasPathFilter.java
  4. 26
      json-path/src/test/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilterTest.java

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

@ -29,13 +29,12 @@ import java.util.regex.Pattern;
*/
public class ArrayEvalFilter extends PathTokenFilter {
private static final Pattern CONDITION_STATEMENT_PATTERN = Pattern.compile("\\[\\s?\\?\\(.*?[!=<>]+.*?\\)\\s?]");
private static final Pattern CONDITION_STATEMENT_PATTERN = Pattern.compile("\\[\\s?\\?\\(.*\\)\\s?]");
private static final Pattern CONDITION_PATTERN = Pattern.compile("\\s?(@.*?)\\s?([!=<>]+)\\s?(.*?)\\s?");
private static final Pattern FIELD_EXISTS_PATTERN = Pattern.compile("\\s?(@.*?)\\s?(.*?)\\s?");
private static final Pattern FIELD_EXISTS_PATTERN = Pattern.compile("\\s?@\\s?(.*?)\\s?");
private static final Pattern HASPATH_PATTERN = Pattern.compile("\\s?(@\\..*)\\s?(.*?)\\s?");
private ConditionStatement[] conditionStatements;
private Expression[] expressions;
public ArrayEvalFilter(String condition) {
super(condition);
@ -48,14 +47,12 @@ public class ArrayEvalFilter extends PathTokenFilter {
String[] split = condition.split("&&");
conditionStatements = new ConditionStatement[split.length];
for(int i = 0; i < split.length; i++){
conditionStatements[i] = createConditionStatement(split[i]);
expressions = new Expression[split.length];
for (int i = 0; i < split.length; i++) {
expressions[i] = createExpression(split[i]);
}
}
@Override
public Object filter(Object obj, Configuration configuration) {
JsonProvider jsonProvider = configuration.getProvider();
@ -67,7 +64,7 @@ public class ArrayEvalFilter extends PathTokenFilter {
}
Object result = jsonProvider.createArray();
for (Object item : src) {
if (isMatch(item, configuration, conditionStatements)) {
if (isMatch(item, configuration, expressions)) {
jsonProvider.setProperty(result, jsonProvider.length(result), item);
}
}
@ -84,44 +81,46 @@ public class ArrayEvalFilter extends PathTokenFilter {
return true;
}
private boolean isMatch(Object check, Configuration configuration, ConditionStatement... conditionStatements) {
try {
for (ConditionStatement conditionStatement : conditionStatements) {
Object value = (check != null) ? conditionStatement.path.read(check, configuration.options(Option.THROW_ON_MISSING_PROPERTY)) : null;
boolean match = ExpressionEvaluator.eval(value, conditionStatement.getOperator(), conditionStatement.getExpected());
private boolean isMatch(Object check, Configuration configuration, Expression... expressions) {
for (Expression expression: expressions) {
boolean match = expression.evaluate(check, configuration);
if(!match){
return false;
}
}
return true;
} catch (PathNotFoundException e){
return false;
} catch (RuntimeException e){
throw e;
}
}
static boolean isConditionStatement(String condition) {
return CONDITION_STATEMENT_PATTERN.matcher(condition).matches();
}
static ConditionStatement createConditionStatement(String condition) {
static Expression createExpression(String condition) {
Matcher conditionMatcher = CONDITION_PATTERN.matcher(condition);
if (conditionMatcher.matches()) {
String property = conditionMatcher.group(1).trim();
String operator = conditionMatcher.group(2).trim();
String expected = conditionMatcher.group(3).trim();
return new ConditionStatement(condition, property, operator, expected);
return new OperatorExpression(condition, property, operator, expected);
}
Matcher hasPathMatcher = HASPATH_PATTERN.matcher(condition);
if (hasPathMatcher.matches()) {
// evaluates @ or @.foo in expressions
return new HasPathExpression(condition);
}
Matcher fieldExistsMatcher = FIELD_EXISTS_PATTERN.matcher(condition);
if (fieldExistsMatcher.matches()) {
// Field exists check, the single '@' in: $.menu.items[?(@ && @.id == 'ViewSVG')].id
return new ConditionStatement(condition, "@.", "!=", "null");
return new OperatorExpression(condition, "@.", "!=", "null");
}
return null;
}
static class ConditionStatement {
static abstract class Expression {
public abstract boolean evaluate(Object check, Configuration configuration);
}
static class OperatorExpression extends Expression {
private final String condition;
private final String field;
private final String operator;
@ -129,7 +128,7 @@ public class ArrayEvalFilter extends PathTokenFilter {
private final JsonPath path;
ConditionStatement(String condition, String field, String operator, String expected) {
OperatorExpression(String condition, String field, String operator, String expected) {
this.condition = condition;
this.field = field;
this.operator = operator;
@ -147,7 +146,7 @@ public class ArrayEvalFilter extends PathTokenFilter {
this.path = JsonPath.compile(this.field.replace("@", "$"));
}
}
ConditionStatement(String field, String operator, String expected) {
OperatorExpression(String field, String operator, String expected) {
this(null, field, operator, expected);
}
@ -171,9 +170,19 @@ public class ArrayEvalFilter extends PathTokenFilter {
return expected;
}
@Override
public boolean evaluate(Object check, Configuration configuration) {
try {
Object value = (check != null) ? path.read(check, configuration.options(Option.THROW_ON_MISSING_PROPERTY)) : null;
return ExpressionEvaluator.eval(value, operator, expected);
} catch (PathNotFoundException e){
return false;
}
}
@Override
public String toString() {
return "ConditionStatement{" +
return "OperatorExpression{" +
"field='" + field + '\'' +
", operator='" + operator + '\'' +
", expected='" + expected + '\'' +
@ -185,7 +194,7 @@ public class ArrayEvalFilter extends PathTokenFilter {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConditionStatement that = (ConditionStatement) o;
OperatorExpression that = (OperatorExpression) o;
if (expected != null ? !expected.equals(that.expected) : that.expected != null) return false;
if (field != null ? !field.equals(that.field) : that.field != null) return false;
@ -201,5 +210,33 @@ public class ArrayEvalFilter extends PathTokenFilter {
result = 31 * result + (expected != null ? expected.hashCode() : 0);
return result;
}
}
static class HasPathExpression extends Expression {
private final JsonPath path;
public HasPathExpression(String condition) {
if(condition.startsWith("@.")){
this.path = JsonPath.compile(condition.replace("@.", "$."));
} else {
this.path = JsonPath.compile(condition.replace("@", "$"));
}
}
@Override
public boolean evaluate(Object obj, Configuration configuration) {
JsonProvider jsonProvider = configuration.getProvider();
if(jsonProvider.isMap(obj)){
try{
path.read(obj, Configuration.builder().options(Option.THROW_ON_MISSING_PROPERTY).jsonProvider(jsonProvider).build());
return true;
} catch (PathNotFoundException e){
return false;
}
}
return false;
}
}
}

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

@ -62,9 +62,6 @@ public class FilterFactory {
if(ArrayEvalFilter.isConditionStatement(pathFragment)){
return new ArrayEvalFilter(pathFragment);
} else if (!pathFragment.contains("=") && !pathFragment.contains("<") && !pathFragment.contains(">")) {
//[?(@.isbn)]
return new HasPathFilter(pathFragment);
} else {
throw new InvalidPathException("Failed to create PathTokenFilter for path fragment: " + pathFragment);
}

72
json-path/src/main/java/com/jayway/jsonpath/internal/filter/HasPathFilter.java

@ -1,72 +0,0 @@
/*
* 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.internal.filter;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.spi.JsonProvider;
/**
* @author Kalle Stenflo
*/
public class HasPathFilter extends PathTokenFilter {
private final JsonPath path;
public HasPathFilter(String condition) {
super(condition);
String trimmedCondition = condition;
if(condition.contains("['")){
trimmedCondition = trimmedCondition.replace("['", ".");
trimmedCondition = trimmedCondition.replace("']", "");
}
this.path = JsonPath.compile(trim(trimmedCondition, 5, 2));
}
@Override
public Object filter(Object obj, Configuration configuration) {
JsonProvider jsonProvider = configuration.getProvider();
//[?(@.isbn)]
Iterable<Object> src = jsonProvider.toIterable(obj);
Object result = jsonProvider.createArray();
for (Object item : src) {
if(jsonProvider.isMap(item)){
try{
path.read(item, Configuration.builder().options(Option.THROW_ON_MISSING_PROPERTY).jsonProvider(jsonProvider).build());
jsonProvider.setProperty(result, jsonProvider.length(result), item);
} catch (PathNotFoundException e){
// the path was not found in the item
}
}
}
return result;
}
@Override
public Object getRef(Object obj, Configuration configuration) {
throw new UnsupportedOperationException();
}
@Override
public boolean isArrayFilter() {
return true;
}
}

26
json-path/src/test/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilterTest.java

@ -30,27 +30,27 @@ public class ArrayEvalFilterTest {
//assertEquals(new ArrayEvalFilter.ConditionStatement("@.length", ">", "0"), ArrayEvalFilter.createConditionStatement("[?(@.length>0)]"));
//int array
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "5"), ArrayEvalFilter.createConditionStatement("@==5"));
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "5"), ArrayEvalFilter.createConditionStatement("@ == 5"));
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "5"), ArrayEvalFilter.createConditionStatement(" @ == 5 "));
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "5"), ArrayEvalFilter.createConditionStatement("@ ==5"));
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "5"), ArrayEvalFilter.createConditionStatement("@== 5 "));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "5"), ArrayEvalFilter.createExpression("@==5"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "5"), ArrayEvalFilter.createExpression("@ == 5"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "5"), ArrayEvalFilter.createExpression(" @ == 5 "));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "5"), ArrayEvalFilter.createExpression("@ ==5"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "5"), ArrayEvalFilter.createExpression("@== 5 "));
//String array
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "one"), ArrayEvalFilter.createConditionStatement("@=='one'"));
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "one monkey"), ArrayEvalFilter.createConditionStatement("@ == 'one monkey' "));
assertEquals(new ArrayEvalFilter.ConditionStatement("@", "==", "two"), ArrayEvalFilter.createConditionStatement("@ == 'two'"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "one"), ArrayEvalFilter.createExpression("@=='one'"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "one monkey"), ArrayEvalFilter.createExpression("@ == 'one monkey' "));
assertEquals(new ArrayEvalFilter.OperatorExpression("@", "==", "two"), ArrayEvalFilter.createExpression("@ == 'two'"));
//Sub item dot notation
assertEquals(new ArrayEvalFilter.ConditionStatement("@.name", "==", "true"), ArrayEvalFilter.createConditionStatement("@.name == true"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@.name", "==", "true"), ArrayEvalFilter.createExpression("@.name == true"));
//Sub item bracket notation
assertEquals(new ArrayEvalFilter.ConditionStatement("@['name']", "==", "true"), ArrayEvalFilter.createConditionStatement("@['name'] == true"));
assertEquals(new ArrayEvalFilter.ConditionStatement("@.['name']", "==", "true"), ArrayEvalFilter.createConditionStatement("@.['name'] == true"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@['name']", "==", "true"), ArrayEvalFilter.createExpression("@['name'] == true"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@.['name']", "==", "true"), ArrayEvalFilter.createExpression("@.['name'] == true"));
//Sub path notation
assertEquals(new ArrayEvalFilter.ConditionStatement("@['name']['age']", "!=", "true"), ArrayEvalFilter.createConditionStatement("@['name']['age'] != true"));
assertEquals(new ArrayEvalFilter.ConditionStatement("@.['name'].age", ">", "true"), ArrayEvalFilter.createConditionStatement("@.['name'].age > true"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@['name']['age']", "!=", "true"), ArrayEvalFilter.createExpression("@['name']['age'] != true"));
assertEquals(new ArrayEvalFilter.OperatorExpression("@.['name'].age", ">", "true"), ArrayEvalFilter.createExpression("@.['name'].age > true"));
}

Loading…
Cancel
Save