Browse Source

Array eval filters can be a path like [?(@.address.street == 'foo')]

pull/11/merge
Kalle Stenflo 11 years ago
parent
commit
81aa8b171d
  1. 5
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  2. 101
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilter.java
  3. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FieldFilter.java
  4. 39
      json-path/src/test/java/com/jayway/jsonpath/IssuesTest.java
  5. 4
      json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java
  6. 42
      json-path/src/test/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilterTest.java

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

@ -208,6 +208,11 @@ public class JsonPath {
JsonProvider jsonProvider = JsonProviderFactory.createProvider();
if(this.getPath().equals("$")){
//This path only references the whole object. No need to do any work here...
return (T)jsonObject;
}
if (!jsonProvider.isMap(jsonObject) && !jsonProvider.isList(jsonObject)) {
throw new IllegalArgumentException("Invalid container object");
}

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

@ -15,6 +15,8 @@
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.filter.eval.ExpressionEvaluator;
import com.jayway.jsonpath.spi.JsonProvider;
@ -28,25 +30,14 @@ import java.util.regex.Pattern;
*/
public class ArrayEvalFilter extends PathTokenFilter {
private static final Pattern PATTERN = Pattern.compile("(.*?)\\s?([=<>]+)\\s?(.*)");
private static final Pattern PATTERN = Pattern.compile("\\[\\s?\\?\\(\\s?(@.*?)\\s?([!=<>]+)\\s?(.*?)\\s?\\)\\s?\\]");
private final ConditionStatement conditionStatement;
public ArrayEvalFilter(String condition) {
super(condition);
//[?(@.isbn == 10)]
String trimmedCondition = condition;
if(condition.contains("['")){
trimmedCondition = trimmedCondition.replace("['", ".");
trimmedCondition = trimmedCondition.replace("']", "");
}
if(trimmedCondition.startsWith("[?(@==")){
trimmedCondition = trim(trimmedCondition, 4, 2);
} else {
trimmedCondition = trim(trimmedCondition, 5, 2);
}
this.conditionStatement = createConditionStatement(trimmedCondition);
this.conditionStatement = createConditionStatement(condition);
}
@Override
@ -78,34 +69,24 @@ public class ArrayEvalFilter extends PathTokenFilter {
}
private boolean isMatch(Object check, ConditionStatement conditionStatement, JsonProvider jsonProvider) {
if (jsonProvider.isMap(check)) {
Map<String, Object> obj = jsonProvider.toMap(check);
if (!obj.containsKey(conditionStatement.getField())) {
return false;
}
Object propertyValue = obj.get(conditionStatement.getField());
if (jsonProvider.isContainer(propertyValue)) {
return false;
}
return ExpressionEvaluator.eval(propertyValue, conditionStatement.getOperator(), conditionStatement.getExpected());
} else if(jsonProvider.isList(check)) {
try {
Object value = conditionStatement.path.read(check);
return ExpressionEvaluator.eval(value, conditionStatement.getOperator(), conditionStatement.getExpected());
} catch (PathNotFoundException e){
return false;
} else {
return ExpressionEvaluator.eval(check, conditionStatement.getOperator(), conditionStatement.getExpected());
} catch (RuntimeException e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
private ConditionStatement createConditionStatement(String str) {
static ConditionStatement createConditionStatement(String str) {
Matcher matcher = PATTERN.matcher(str);
if (matcher.matches()) {
String property = matcher.group(1);
String operator = matcher.group(2);
String expected = matcher.group(3);
String property = matcher.group(1).trim();
String operator = matcher.group(2).trim();
String expected = matcher.group(3).trim();
return new ConditionStatement(property, operator, expected);
} else {
@ -113,14 +94,16 @@ public class ArrayEvalFilter extends PathTokenFilter {
}
}
private static class ConditionStatement {
static class ConditionStatement {
private final String field;
private final String operator;
private final String expected;
private final JsonPath path;
private ConditionStatement(String field, String operator, String expected) {
ConditionStatement(String field, String operator, String expected) {
this.field = field;
this.operator = operator.trim();
this.operator = operator;
if(expected.startsWith("'")){
@ -128,6 +111,17 @@ public class ArrayEvalFilter extends PathTokenFilter {
}else{
this.expected = expected;
}
if(field.startsWith("@.")){
this.path = JsonPath.compile(this.field.replace("@.", "$."));
} else {
this.path = JsonPath.compile(this.field.replace("@", "$"));
}
}
public JsonPath getJsonPath() {
return path;
}
public String getField() {
@ -141,5 +135,36 @@ public class ArrayEvalFilter extends PathTokenFilter {
public String getExpected() {
return expected;
}
@Override
public String toString() {
return "ConditionStatement{" +
"field='" + field + '\'' +
", operator='" + operator + '\'' +
", expected='" + expected + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConditionStatement that = (ConditionStatement) 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;
if (operator != null ? !operator.equals(that.operator) : that.operator != null) return false;
return true;
}
@Override
public int hashCode() {
int result = field != null ? field.hashCode() : 0;
result = 31 * result + (operator != null ? operator.hashCode() : 0);
result = 31 * result + (expected != null ? expected.hashCode() : 0);
return result;
}
}
}

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

@ -70,7 +70,7 @@ public class FieldFilter extends PathTokenFilter {
}
return result;
}
} else {
} else if (jsonProvider.isMap(obj)){
Map<String, Object> map = jsonProvider.toMap(obj);
if(!map.containsKey(condition) && split.length == 1){
@ -91,6 +91,8 @@ public class FieldFilter extends PathTokenFilter {
}
} else {
throw new PathNotFoundException();
}
}

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

@ -1,15 +1,12 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.IOUtils;
import com.jayway.jsonpath.spi.JsonProviderFactory;
import com.jayway.jsonpath.spi.impl.JacksonProvider;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
import java.util.regex.Matcher;
import java.util.Map;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@ -161,12 +158,40 @@ public class IssuesTest {
@Test
public void issue_32(){
String json = "{\"text\" : \"skill: \\\"Heuristic Evaluation\\\"\", \"country\" : \"\"}";
assertEquals("skill: \"Heuristic Evaluation\"", JsonPath.read(json, "$.text"));
}
@Test
public void issue_33(){
String json = "{ \"store\": {\n" +
" \"book\": [ \n" +
" { \"category\": \"reference\",\n" +
" \"author\": {\n" +
" \"name\": \"Author Name\",\n" +
" \"age\": 36\n" +
" },\n" +
" \"title\": \"Sayings of the Century\",\n" +
" \"price\": 8.95\n" +
" },\n" +
" { \"category\": \"fiction\",\n" +
" \"author\": \"Evelyn Waugh\",\n" +
" \"title\": \"Sword of Honour\",\n" +
" \"price\": 12.99,\n" +
" \"isbn\": \"0-553-21311-3\"\n" +
" }\n" +
" ],\n" +
" \"bicycle\": {\n" +
" \"color\": \"red\",\n" +
" \"price\": 19.95\n" +
" }\n" +
" }\n" +
"}";
List<Map<String, Object>> result = JsonPath.read(json, "$.store.book[?(@.author.age == 36)]");
System.out.println(result);
}
}

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

@ -1,6 +1,5 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.spi.JsonProviderFactory;
import com.jayway.jsonpath.util.ScriptEngineJsonPath;
import org.junit.Test;
@ -245,7 +244,8 @@ public class JsonPathTest {
@Test
public void dots_in_predicate_works() throws Exception {
assertThat(JsonPath.<List<String>>read(PRODUCT_JSON, "$.product[?(@.attr.with.dot=='A')].codename"), hasItems("Seattle"));
assertThat(JsonPath.<List<String>>read(PRODUCT_JSON, "$.product[?(@.['attr.with.dot']=='A')].codename"), hasItems("Seattle"));
//assertThat(JsonPath.<List<String>>read(PRODUCT_JSON, "$.product[?(@.attr.with.dot=='A')].codename"), hasItems("Seattle"));
}

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

@ -0,0 +1,42 @@
package com.jayway.jsonpath.internal.filter;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* User: kalle
* Date: 8/21/13
* Time: 12:21 PM
*/
public class ArrayEvalFilterTest {
@Test
public void condition_statements_can_be_parsed() {
//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 ) ]"));
//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')]"));
//Sub item dot notation
assertEquals(new ArrayEvalFilter.ConditionStatement("@.name", "==", "true"), ArrayEvalFilter.createConditionStatement("[?(@.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)]"));
//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)]"));
}
}
Loading…
Cancel
Save