Browse Source

rework JsonProvider so that the backing object don't have to implement List or Map

pull/29/head
Jochen Berger 12 years ago
parent
commit
41ead56385
  1. 6
      json-path/src/main/java/com/jayway/jsonpath/Filter.java
  2. 4
      json-path/src/main/java/com/jayway/jsonpath/JsonModel.java
  3. 2
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  4. 20
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayEvalFilter.java
  5. 28
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java
  6. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayQueryFilter.java
  7. 51
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FieldFilter.java
  8. 13
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/HasFieldFilter.java
  9. 23
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ScanFilter.java
  10. 16
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/WildcardFilter.java
  11. 65
      json-path/src/main/java/com/jayway/jsonpath/spi/JsonProvider.java
  12. 111
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/AbstractJsonProvider.java
  13. 2
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/JacksonProvider.java
  14. 2
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/JsonSmartJsonProvider.java

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

@ -50,11 +50,11 @@ public abstract class Filter<T> {
* @param jsonProvider the json provider that is used to create the result list
* @return the filtered list
*/
public List doFilter(List<T> filterItems, JsonProvider jsonProvider) {
List result = jsonProvider.createList();;
public Object doFilter(Iterable<T> filterItems, JsonProvider jsonProvider) {
Object result = jsonProvider.createArray();
for (T filterItem : filterItems) {
if (accept(filterItem)) {
result.add(filterItem);
jsonProvider.setProperty(result, jsonProvider.length(result), filterItem);
}
}
return result;

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

@ -111,7 +111,7 @@ public class JsonModel {
* @return true if root is an array
*/
public boolean isList() {
return jsonProvider.isList(jsonObject);
return jsonProvider.isArray(jsonObject);
}
/**
@ -249,7 +249,7 @@ public class JsonModel {
* @return array operations for this JsonModel
*/
public ArrayOps opsForArray() {
isTrue(jsonProvider.isList(jsonObject), "This JsonModel is not a JSON array");
isTrue(jsonProvider.isArray(jsonObject), "This JsonModel is not a JSON array");
return opsForArray(JSON_PATH_ROOT);
}

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

@ -208,7 +208,7 @@ public class JsonPath {
JsonProvider jsonProvider = JsonProviderFactory.createProvider();
if (!jsonProvider.isMap(jsonObject) && !jsonProvider.isList(jsonObject)) {
if (!jsonProvider.isMap(jsonObject) && !jsonProvider.isArray(jsonObject)) {
throw new IllegalArgumentException("Invalid container object");
}
LinkedList<Filter> contextFilters = new LinkedList<Filter>(filters);

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

@ -18,8 +18,7 @@ import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.filter.eval.ExpressionEvaluator;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -51,17 +50,16 @@ public class ArrayEvalFilter extends PathTokenFilter {
@Override
public Object filter(Object obj, JsonProvider jsonProvider) {
List<Object> src = null;
Iterable<Object> src = null;
try {
src = jsonProvider.toList(obj);
src = jsonProvider.toIterable(obj);
} catch (ClassCastException e){
throw new InvalidPathException("The path fragment '" + this.condition + "' can not be applied to a JSON object only a JSON array.", e);
}
List<Object> result = jsonProvider.createList();
Object result = jsonProvider.createArray();
for (Object item : src) {
if (isMatch(item, conditionStatement, jsonProvider)) {
result.add(item);
jsonProvider.setProperty(result, jsonProvider.length(result), item);
}
}
return result;
@ -79,19 +77,19 @@ 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);
Collection<String> keys = jsonProvider.getPropertyKeys(check);
if (!obj.containsKey(conditionStatement.getField())) {
if (!keys.contains(conditionStatement.getField())) {
return false;
}
Object propertyValue = obj.get(conditionStatement.getField());
Object propertyValue = jsonProvider.getProperty(check, conditionStatement.getField());
if (jsonProvider.isContainer(propertyValue)) {
return false;
}
return ExpressionEvaluator.eval(propertyValue, conditionStatement.getOperator(), conditionStatement.getExpected());
} else if(jsonProvider.isList(check)) {
} else if(jsonProvider.isArray(check)) {
return false;
} else {
return ExpressionEvaluator.eval(check, conditionStatement.getOperator(), conditionStatement.getExpected());

28
json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java

@ -14,10 +14,8 @@
*/
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.List;
import java.util.regex.Pattern;
/**
@ -52,17 +50,16 @@ public class ArrayIndexFilter extends PathTokenFilter {
@Override
public Object filter(Object obj,JsonProvider jsonProvider) {
public Object filter(Object obj, JsonProvider jsonProvider) {
List<Object> src = jsonProvider.toList(obj);
List<Object> result = jsonProvider.createList();
Object result = jsonProvider.createArray();
if (trimmedCondition.contains(OPERATOR)) {
if (trimmedCondition.startsWith(OPERATOR)) {
String trimmedCondition = trim(this.trimmedCondition, 1, 0);
int get = Integer.parseInt(trimmedCondition);
for (int i = 0; i < get; i++) {
result.add(src.get(i));
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, i));
}
return result;
@ -73,13 +70,13 @@ public class ArrayIndexFilter extends PathTokenFilter {
if(get > 0){
get = get * -1;
}
return src.get(src.size() + get);
return jsonProvider.getProperty(obj, jsonProvider.length(obj) + get);
} else {
int start = src.size() + get;
int stop = src.size();
int start = jsonProvider.length(obj) + get;
int stop = jsonProvider.length(obj);
for (int i = start; i < stop; i ++){
result.add(src.get(i));
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, i));
}
return result;
}
@ -91,23 +88,23 @@ public class ArrayIndexFilter extends PathTokenFilter {
int stop = Integer.parseInt(indexes[1]);
for (int i = start; i < stop; i ++){
result.add(src.get(i));
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, i));
}
return result;
}
} else {
String[] indexArr = COMMA.split(trimmedCondition);
if(src.isEmpty()){
if(jsonProvider.length(obj) == 0){
return result;
}
if (indexArr.length == 1) {
return src.get(Integer.parseInt(indexArr[0]));
return jsonProvider.getProperty(obj, indexArr[0]);
} else {
for (String idx : indexArr) {
result.add(src.get(Integer.parseInt(idx.trim())));
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, idx.trim()));
}
return result;
}
@ -118,8 +115,7 @@ public class ArrayIndexFilter extends PathTokenFilter {
public Object getRef(Object obj, JsonProvider jsonProvider) {
if(SINGLE_ARRAY_INDEX_PATTERN.matcher(condition).matches()){
String trimmedCondition = trim(condition, 1, 1);
List<Object> src = jsonProvider.toList(obj);
return src.get(Integer.parseInt(trimmedCondition));
return jsonProvider.getProperty(obj, trimmedCondition);
} else {
throw new UnsupportedOperationException();

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

@ -18,7 +18,6 @@ import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.LinkedList;
import java.util.List;
/**
* @author Kalle Stenflo
@ -34,7 +33,7 @@ public class ArrayQueryFilter extends PathTokenFilter {
Filter filter = filters.poll();
return filter.doFilter(jsonProvider.toList(obj), jsonProvider);
return filter.doFilter(jsonProvider.toIterable(obj), jsonProvider);
}

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

@ -15,13 +15,11 @@
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author Kalle Stenflo
@ -37,34 +35,35 @@ public class FieldFilter extends PathTokenFilter {
@Override
public Object filter(Object obj, JsonProvider jsonProvider, LinkedList<Filter> filters, boolean inArrayContext) {
if (jsonProvider.isList(obj)) {
if (jsonProvider.isArray(obj)) {
if (!inArrayContext) {
throw new PathNotFoundException("Trying to access the field '" + condition +"' in an array context.");
} else {
List<Object> result = jsonProvider.createList();
for (Object current : jsonProvider.toList(obj)) {
Object result = jsonProvider.createArray();
for (Object current : jsonProvider.toIterable(obj)) {
if (jsonProvider.isMap(current)) {
Map<String, Object> map = jsonProvider.toMap(current);
Collection<String> keys = jsonProvider.getPropertyKeys(current);
if(split.length == 1){
if (map.containsKey(condition)) {
Object o = map.get(condition);
if (jsonProvider.isList(o)) {
result.addAll(jsonProvider.toList(o));
if (keys.contains(condition)) {
Object o = jsonProvider.getProperty(current, condition);
if (jsonProvider.isArray(o)) {
for(Object item : jsonProvider.toIterable(o)){
jsonProvider.setProperty(result, jsonProvider.length(result), item);
}
} else {
result.add(map.get(condition));
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(current, condition));
}
}
} else {
Map<String, Object> res = jsonProvider.createMap();
Object res = jsonProvider.createMap();
for (String prop : split) {
if (map.containsKey(prop)) {
res.put(prop, map.get(prop));
if (keys.contains(prop)) {
jsonProvider.setProperty(res, prop, jsonProvider.getProperty(current, prop));
}
}
result.add(res);
jsonProvider.setProperty(result, jsonProvider.length(result), res);
}
}
}
@ -72,18 +71,18 @@ public class FieldFilter extends PathTokenFilter {
}
} else {
Map<String, Object> map = jsonProvider.toMap(obj);
if(!map.containsKey(condition) && split.length == 1){
Collection<String> keys = jsonProvider.getPropertyKeys(obj);
if(!keys.contains(condition) && split.length == 1){
throw new PathNotFoundException("Path '" + condition + "' not found in the current context.");
} else {
if(split.length == 1){
return map.get(condition);
return jsonProvider.getProperty(obj, condition);
} else {
Map<String, Object> res = jsonProvider.createMap();
Object res = jsonProvider.createMap();
for (String prop : split) {
if(map.containsKey(prop)){
res.put(prop, map.get(prop));
if(keys.contains(prop)){
jsonProvider.setProperty(res, prop, jsonProvider.getProperty(obj, prop));
}
}
return res;
@ -96,10 +95,10 @@ public class FieldFilter extends PathTokenFilter {
public Object filter(Object obj, JsonProvider jsonProvider) {
if (jsonProvider.isList(obj)) {
if (jsonProvider.isArray(obj)) {
return obj;
} else {
return jsonProvider.getMapValue(obj, condition);
return jsonProvider.getProperty(obj, condition);
}
}

13
json-path/src/main/java/com/jayway/jsonpath/internal/filter/HasFieldFilter.java

@ -16,8 +16,7 @@ package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* @author Kalle Stenflo
@ -42,14 +41,14 @@ public class HasFieldFilter extends PathTokenFilter {
public Object filter(Object obj, JsonProvider jsonProvider) {
//[?(@.isbn)]
List<Object> src = jsonProvider.toList(obj);
List<Object> result = jsonProvider.createList();
Iterable<Object> src = jsonProvider.toIterable(obj);
Object result = jsonProvider.createArray();
for (Object item : src) {
if(jsonProvider.isMap(item)){
Map<String, Object> map = jsonProvider.toMap(item);
if(map.containsKey(trimmedCondition)){
result.add(map);
Collection<String> keys = jsonProvider.getPropertyKeys(item);
if(keys.contains(trimmedCondition)){
jsonProvider.setProperty(result, jsonProvider.length(result), item);
}
}
}

23
json-path/src/main/java/com/jayway/jsonpath/internal/filter/ScanFilter.java

@ -17,8 +17,6 @@ package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.List;
/**
* @author Kalle Stenflo
*/
@ -30,7 +28,7 @@ public class ScanFilter extends PathTokenFilter {
@Override
public Object filter(Object obj, JsonProvider jsonProvider) {
List<Object> result = jsonProvider.createList();
Object result = jsonProvider.createArray();
scan(obj, result, jsonProvider);
return result;
@ -47,22 +45,15 @@ public class ScanFilter extends PathTokenFilter {
}
private void scan(Object container, List<Object> result, JsonProvider jsonProvider) {
private void scan(Object container, Object result, JsonProvider jsonProvider) {
if (jsonProvider.isMap(container)) {
result.add(container);
for (Object value : jsonProvider.toMap(container).values()) {
if (jsonProvider.isContainer(value)) {
scan(value, result, jsonProvider);
}
}
} else if (jsonProvider.isList(container)) {
jsonProvider.setProperty(result, jsonProvider.length(result), container);
}
for (Object value : jsonProvider.toList(container)) {
if (jsonProvider.isContainer(value)) {
scan(value, result, jsonProvider);
}
for (Object value : jsonProvider.toIterable(container)) {
if (jsonProvider.isContainer(value)) {
scan(value, result, jsonProvider);
}
}
}

16
json-path/src/main/java/com/jayway/jsonpath/internal/filter/WildcardFilter.java

@ -16,8 +16,6 @@ package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.List;
/**
* @author Kalle Stenflo
*/
@ -29,17 +27,17 @@ public class WildcardFilter extends PathTokenFilter {
@Override
public Object filter(Object obj, JsonProvider jsonProvider) {
List<Object> result = jsonProvider.createList();
Object result = jsonProvider.createArray();
if (jsonProvider.isList(obj)) {
for (Object current : jsonProvider.toList(obj)) {
for (Object value : jsonProvider.toMap(current).values()) {
result.add(value);
if (jsonProvider.isArray(obj)) {
for (Object current : jsonProvider.toIterable(obj)) {
for (Object value : jsonProvider.toIterable(current)) {
jsonProvider.setProperty(result, jsonProvider.length(result), value);
}
}
} else {
for (Object value : jsonProvider.toMap(obj).values()) {
result.add(value);
for (Object value : jsonProvider.toIterable(obj)) {
jsonProvider.setProperty(result, jsonProvider.length(result), value);
}
}
return result;

65
json-path/src/main/java/com/jayway/jsonpath/spi/JsonProvider.java

@ -18,8 +18,7 @@ import com.jayway.jsonpath.InvalidJsonException;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* @author Kalle Stenflo
@ -37,60 +36,76 @@ public interface JsonProvider {
String toJson(Object obj);
Map<String, Object> createMap();
Object createMap();
List<Object> createList();
Iterable createArray();
Object clone(Object model);
/**
* checks if object is <code>instanceof</code> <code>java.util.List</code> or <code>java.util.Map</code>
* checks if object is a map or an array
*
* @param obj object to check
* @return true if List or Map
* @return true if obj is a map or an array
*/
boolean isContainer(Object obj);
/**
* checks if object is <code>instanceof</code> <code>java.util.List</code>
* checks if object is an array
*
* @param obj object to check
* @return true if List
* @return true if obj is an array
*/
boolean isList(Object obj);
boolean isArray(Object obj);
/**
* Converts give object to a List
* Get the length of an array or object
* @param obj an array or an object
* @return the number of entries in the array or object
*/
int length(Object obj);
/**
* Converts given object to an {@link Iterable}
*
* @param list
* @return
* @param obj an array or an object
* @return the entries for an array or the values for a map
*/
List<Object> toList(Object list);
Iterable<Object> toIterable(Object obj);
/**
* Converts given object to a Map
* Returns the keys from the given object or the indexes from an array
*
* @param map
* @return
* @param obj an array or an object
* @return the keys for an object or the indexes for an array
*/
Map<String, Object> toMap(Object map);
Collection<String> getPropertyKeys(Object obj);
/**
* Extracts a value from a Map
* Extracts a value from an object or array
*
* @param map
* @param key
* @return
* @param obj an array or an object
* @param key a String key or a numerical index
* @return the entry at the given key, i.e. obj[key]
*/
Object getMapValue(Object map, String key);
Object getProperty(Object obj, Object key);
/**
* Sets a value in an object or array
*
* @param obj an array or an object
* @param key a String key or a numerical index
* @param value the value to set
*/
void setProperty(Object obj, Object key, Object value);
/**
* checks if object is <code>instanceof</code> <code>java.util.Map</code>
* checks if object is a map (i.e. no array)
*
* @param obj object to check
* @return true if Map
* @return true if the object is a map
*/
boolean isMap(Object obj);

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

@ -15,9 +15,12 @@
package com.jayway.jsonpath.spi.impl;
import com.jayway.jsonpath.spi.JsonProvider;
import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -32,67 +35,111 @@ public abstract class AbstractJsonProvider implements JsonProvider {
}
/**
* checks if object is <code>instanceof</code> <code>java.util.List</code> or <code>java.util.Map</code>
* checks if object is a map or an array
*
* @param obj object to check
* @return true if List or Map
* @return true if obj is a map or an array
*/
public boolean isContainer(Object obj) {
return (isList(obj) || isMap(obj));
return (isArray(obj) || isMap(obj));
}
/**
* checks if object is <code>instanceof</code> <code>java.util.List</code>
* checks if object is an array
*
* @param obj object to check
* @return true if List
* @return true if obj is an array
*/
public boolean isList(Object obj) {
public boolean isArray(Object obj) {
return (obj instanceof List);
}
/**
* Converts give object to a List
* Extracts a value from an object or array
*
* @param list object to convert
* @return object as list
* @param obj an array or an object
* @param key a String key or a numerical index
* @return the entry at the given key, i.e. obj[key]
*/
@SuppressWarnings({"unchecked"})
public List<Object> toList(Object list) {
return (List<Object>) list;
public Object getProperty(Object obj, Object key) {
if (isMap(obj))
return ((Map) obj).get(key.toString());
else{
int index = key instanceof Integer? (Integer) key : Integer.parseInt(key.toString());
return ((List) obj).get(index);
}
}
/**
* Converts given object to a Map
* Sets a value in an object or array
*
* @param map object to convert
* @return object as map
* @param obj an array or an object
* @param key a String key or a numerical index
* @param value the value to set
*/
@SuppressWarnings({"unchecked"})
public Map<String, Object> toMap(Object map) {
return (Map<String, Object>) map;
public void setProperty(Object obj, Object key, Object value) {
if (isMap(obj))
((Map) obj).put(key.toString(), value);
else{
int index = key instanceof Integer? (Integer) key : Integer.parseInt(key.toString());
((List) obj).add(index, value);
}
}
/**
* Extracts a value from a Map
* checks if object is a map (i.e. no array)
*
* @param map map to read from
* @param key key to read
* @return value of key in map
* @param obj object to check
* @return true if the object is a map
*/
public Object getMapValue(Object map, String key) {
return toMap(map).get(key);
public boolean isMap(Object obj) {
return (obj instanceof Map);
}
/**
* checks if object is <code>instanceof</code> <code>java.util.Map</code>
* Returns the keys from the given object or the indexes from an array
*
* @param obj object to check
* @return true if Map
* @param obj an array or an object
* @return the keys for an object or the indexes for an array
*/
public boolean isMap(Object obj) {
return (obj instanceof Map);
public Collection<String> getPropertyKeys(Object obj) {
if (isArray(obj)){
List l = (List) obj;
List<String> keys = new ArrayList<String>(l.size());
for (int i = 0; i < l.size(); i++){
keys.add(String.valueOf(i));
}
return keys;
}
else{
return ((Map)obj).keySet();
}
}
/**
* Get the length of an array or object
* @param obj an array or an object
* @return the number of entries in the array or object
*/
public int length(Object obj) {
if (isArray(obj)){
return ((List)obj).size();
}
return getPropertyKeys(obj).size();
}
/**
* Converts given object to an {@link Iterable}
*
* @param obj an array or an object
* @return the entries for an array or the values for a map
*/
public Iterable<Object> toIterable(Object obj) {
if (isArray(obj))
return ((Iterable) obj);
else
return ((Map)obj).values();
}
}

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

@ -85,7 +85,7 @@ public class JacksonProvider extends AbstractJsonProvider implements MappingProv
}
@Override
public List<Object> createList() {
public List<Object> createArray() {
return new LinkedList<Object>();
}

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

@ -52,7 +52,7 @@ public class JsonSmartJsonProvider extends AbstractJsonProvider {
return containerFactory.createObjectContainer();
}
public List<Object> createList() {
public List<Object> createArray() {
return containerFactory.createArrayContainer();
}

Loading…
Cancel
Save