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(); 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)) { if (!jsonProvider.isMap(jsonObject) && !jsonProvider.isList(jsonObject)) {
throw new IllegalArgumentException("Invalid container object"); 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; package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.InvalidPathException; 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.internal.filter.eval.ExpressionEvaluator;
import com.jayway.jsonpath.spi.JsonProvider; import com.jayway.jsonpath.spi.JsonProvider;
@ -28,25 +30,14 @@ import java.util.regex.Pattern;
*/ */
public class ArrayEvalFilter extends PathTokenFilter { 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; private final ConditionStatement conditionStatement;
public ArrayEvalFilter(String condition) { public ArrayEvalFilter(String condition) {
super(condition); super(condition);
//[?(@.isbn == 10)] //[?(@.isbn == 10)]
this.conditionStatement = createConditionStatement(condition);
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);
} }
@Override @Override
@ -78,34 +69,24 @@ public class ArrayEvalFilter extends PathTokenFilter {
} }
private boolean isMatch(Object check, ConditionStatement conditionStatement, JsonProvider jsonProvider) { 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()); try {
Object value = conditionStatement.path.read(check);
if (jsonProvider.isContainer(propertyValue)) { return ExpressionEvaluator.eval(value, conditionStatement.getOperator(), conditionStatement.getExpected());
return false; } catch (PathNotFoundException e){
}
return ExpressionEvaluator.eval(propertyValue, conditionStatement.getOperator(), conditionStatement.getExpected());
} else if(jsonProvider.isList(check)) {
return false; return false;
} else { } catch (RuntimeException e){
return ExpressionEvaluator.eval(check, conditionStatement.getOperator(), conditionStatement.getExpected()); e.printStackTrace();
throw new RuntimeException(e);
} }
} }
static ConditionStatement createConditionStatement(String str) {
private ConditionStatement createConditionStatement(String str) {
Matcher matcher = PATTERN.matcher(str); Matcher matcher = PATTERN.matcher(str);
if (matcher.matches()) { if (matcher.matches()) {
String property = matcher.group(1); String property = matcher.group(1).trim();
String operator = matcher.group(2); String operator = matcher.group(2).trim();
String expected = matcher.group(3); String expected = matcher.group(3).trim();
return new ConditionStatement(property, operator, expected); return new ConditionStatement(property, operator, expected);
} else { } else {
@ -113,14 +94,16 @@ public class ArrayEvalFilter extends PathTokenFilter {
} }
} }
private static class ConditionStatement { static class ConditionStatement {
private final String field; private final String field;
private final String operator; private final String operator;
private final String expected; 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.field = field;
this.operator = operator.trim(); this.operator = operator;
if(expected.startsWith("'")){ if(expected.startsWith("'")){
@ -128,6 +111,17 @@ public class ArrayEvalFilter extends PathTokenFilter {
}else{ }else{
this.expected = expected; 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() { public String getField() {
@ -141,5 +135,36 @@ public class ArrayEvalFilter extends PathTokenFilter {
public String getExpected() { public String getExpected() {
return expected; 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; return result;
} }
} else { } else if (jsonProvider.isMap(obj)){
Map<String, Object> map = jsonProvider.toMap(obj); Map<String, Object> map = jsonProvider.toMap(obj);
if(!map.containsKey(condition) && split.length == 1){ 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; package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.IOUtils; 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.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.Map;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
@ -161,12 +158,40 @@ public class IssuesTest {
@Test @Test
public void issue_32(){ public void issue_32(){
String json = "{\"text\" : \"skill: \\\"Heuristic Evaluation\\\"\", \"country\" : \"\"}"; String json = "{\"text\" : \"skill: \\\"Heuristic Evaluation\\\"\", \"country\" : \"\"}";
assertEquals("skill: \"Heuristic Evaluation\"", JsonPath.read(json, "$.text")); 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; package com.jayway.jsonpath;
import com.jayway.jsonpath.spi.JsonProviderFactory;
import com.jayway.jsonpath.util.ScriptEngineJsonPath; import com.jayway.jsonpath.util.ScriptEngineJsonPath;
import org.junit.Test; import org.junit.Test;
@ -245,7 +244,8 @@ public class JsonPathTest {
@Test @Test
public void dots_in_predicate_works() throws Exception { 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