Browse Source

Docs and refactorings.

pull/8/merge
Kalle Stenflo 12 years ago
parent
commit
24e8311c9c
  1. 12
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  2. 14
      json-path/src/main/java/com/jayway/jsonpath/InvalidConversionException.java
  3. 627
      json-path/src/main/java/com/jayway/jsonpath/JsonModel.java
  4. 18
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  5. 30
      json-path/src/main/java/com/jayway/jsonpath/Transformer.java
  6. 78
      json-path/src/main/java/com/jayway/jsonpath/internal/ConvertUtils.java
  7. 32
      json-path/src/main/java/com/jayway/jsonpath/internal/IOUtils.java
  8. 21
      json-path/src/main/java/com/jayway/jsonpath/internal/PathToken.java
  9. 12
      json-path/src/main/java/com/jayway/jsonpath/internal/PathTokenizer.java
  10. 10
      json-path/src/main/java/com/jayway/jsonpath/internal/Util.java
  11. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java
  12. 7
      json-path/src/main/java/com/jayway/jsonpath/spi/HttpProvider.java
  13. 28
      json-path/src/main/java/com/jayway/jsonpath/spi/HttpProviderFactory.java
  14. 2
      json-path/src/main/java/com/jayway/jsonpath/spi/JsonProvider.java
  15. 1
      json-path/src/main/java/com/jayway/jsonpath/spi/JsonProviderFactory.java
  16. 7
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/AbstractJsonProvider.java
  17. 12
      json-path/src/main/java/com/jayway/jsonpath/spi/impl/JacksonProvider.java
  18. 30
      json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java
  19. 38
      json-path/src/test/java/com/jayway/jsonpath/HttpProviderTest.java
  20. 145
      json-path/src/test/java/com/jayway/jsonpath/JsonModelOpsTest.java
  21. 65
      json-path/src/test/java/com/jayway/jsonpath/JsonModelSubModelDetachedTest.java
  22. 116
      json-path/src/test/java/com/jayway/jsonpath/JsonModelSubModelTest.java
  23. 21
      json-path/src/test/java/com/jayway/jsonpath/JsonModelTest.java
  24. 20
      json-path/src/test/java/com/jayway/jsonpath/JsonProviderTest.java
  25. 13
      json-path/src/test/java/com/jayway/jsonpath/PathTest.java

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

@ -27,11 +27,6 @@ import static org.apache.commons.lang.Validate.notNull;
public class Criteria {
/**
* Custom "not-null" object as we have to be able to work with {@literal null} values as well.
*/
private static final Object NOT_SET = new Object();
private enum CriteriaType {
GT,
GTE,
@ -48,6 +43,11 @@ public class Criteria {
OR
}
/**
* Custom "not-null" object as we have to be able to work with {@literal null} values as well.
*/
private static final Object NOT_SET = new Object();
private final String key;
private final List<Criteria> criteriaChain;
@ -234,8 +234,6 @@ public class Criteria {
}
return true;
} else {
if (isValue == null) {
return (map.get(key) == null);
} else {

14
json-path/src/main/java/com/jayway/jsonpath/InvalidConversionException.java

@ -0,0 +1,14 @@
package com.jayway.jsonpath;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
* Date: 3/12/12
* Time: 7:49 AM
*/
public class InvalidConversionException extends RuntimeException {
public InvalidConversionException(String message) {
super(message);
}
}

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

@ -14,8 +14,9 @@
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.ConvertUtils;
import com.jayway.jsonpath.internal.PathToken;
import com.jayway.jsonpath.internal.Util;
import com.jayway.jsonpath.internal.IOUtils;
import com.jayway.jsonpath.spi.JsonProvider;
import com.jayway.jsonpath.spi.JsonProviderFactory;
import com.jayway.jsonpath.spi.MappingProviderFactory;
@ -26,17 +27,18 @@ import java.net.URL;
import java.util.*;
import static java.util.Arrays.asList;
import static org.apache.commons.lang.Validate.notEmpty;
import static org.apache.commons.lang.Validate.notNull;
import static org.apache.commons.lang.Validate.*;
/**
* A JsonModel represents a parsed JSON document that provides easy and efficient read operations. In contrast to the
* A JsonModel holds a parsed JSON document and provides easy read and write operations. In contrast to the
* static read operations provided by {@link JsonPath} a JsonModel will only parse the document once.
*
* @author Kalle Stenflo
*/
public class JsonModel {
private static final JsonPath JSON_PATH_ROOT = JsonPath.compile("$");
private Object jsonObject;
private JsonProvider jsonProvider;
@ -98,23 +100,71 @@ public class JsonModel {
this.jsonObject = jsonProvider.parse(jsonInputStream);
this.jsonProvider = jsonProvider;
} finally {
Util.closeQuietly(jsonInputStream);
IOUtils.closeQuietly(jsonInputStream);
}
}
public boolean isList(){
/**
* Check if this JsonModel is holding a JSON array as to object
*
* @return true if root is an array
*/
public boolean isList() {
return jsonProvider.isList(jsonObject);
}
public boolean isMap(){
/**
* Check if this JsonModel is holding a JSON object as to object
*
* @return true if root is an object
*/
public boolean isMap() {
return jsonProvider.isMap(jsonObject);
}
/**
* Check if this JsonModel has the given definite path
*
* @see com.jayway.jsonpath.JsonPath#isPathDefinite()
*
* @param jsonPath path to check
* @return true if model contains path
*/
public boolean hasPath(String jsonPath){
return hasPath(JsonPath.compile(jsonPath));
}
/**
* Check if this JsonModel has the given definite path
*
* @see com.jayway.jsonpath.JsonPath#isPathDefinite()
*
* @param jsonPath path to check
* @return true if model contains path
*/
public boolean hasPath(JsonPath jsonPath){
isTrue(jsonPath.isPathDefinite(), "hasPath can only be used for definite paths");
try {
get(jsonPath);
} catch(InvalidPathException e){
return false;
}
return true;
}
// --------------------------------------------------------
//
// Getters
//
// --------------------------------------------------------
/**
* Returns the root object of this JsonModel
*
* @return returns the root object
*/
public Object getJsonObject() {
return this.jsonObject;
}
@ -125,11 +175,51 @@ public class JsonModel {
//
// --------------------------------------------------------
/**
* Reads the given path from this JsonModel. Filters is a way to problematically filter the contents of a list.
* Instead of writing the filter criteria directly inside the JsonPath expression the filter is indicated and
* provided as an argument.
* <p/>
* All three statements below are equivalent
* <p/>
* <code>
* JsonModel model = JsonModel.model(myJson);
* <p/>
* //A
* List<String> books = model.read("$store.book[?(@author == 'Nigel Rees')]");
* <p/>
* //B
* List<String> books = model.read("$store.book[?]", filter(where("author").is("Nigel Rees"));
* <p/>
* //C
* JsonPath path = JsonPath.compile("$store.book[?]", filter(where("author").is("Nigel Rees"));
* <p/>
* List<String> books = model.read(path);
* <p/>
* </code>
* <p/>
* The filters are applied in the order they are provided. If a path contains multiple [?] filter markers
* the filters must be passed in the correct order.
*
* @param jsonPath the path to read
* @param filters filters to use in the path
* @param <T> expected return type
* @return the json path result
* @see Filter
* @see Criteria
*/
@SuppressWarnings({"unchecked"})
public <T> T get(String jsonPath, Filter... filters) {
return (T) get(JsonPath.compile(jsonPath, filters));
}
/**
* Reads the given path from this JsonModel.
*
* @param jsonPath the path to read
* @param <T> expected return type
* @return the json path result
*/
@SuppressWarnings({"unchecked"})
public <T> T get(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
@ -142,36 +232,80 @@ public class JsonModel {
// Model writers
//
// --------------------------------------------------------
public ArrayOps opsForArray(String jsonPath) {
return opsForArray(JsonPath.compile(jsonPath));
}
/**
* Gets an {@link ArrayOps} for this JsonModel. Note that the root element of this model
* must be a json array.
*
* @return array operations for this JsonModel
*/
public ArrayOps opsForArray() {
return new DefaultArrayOps(this.jsonObject);
isTrue(jsonProvider.isList(jsonObject), "This JsonModel is not a JSON array");
return opsForArray(JSON_PATH_ROOT);
}
/**
* Gets an {@link ArrayOps} for the array inside this JsonModel identified by the given JsonPath. The path must
* be definite ({@link com.jayway.jsonpath.JsonPath#isPathDefinite()}).
* <p/>
* Note that the element returned by the given path must be a json array.
*
* @param jsonPath definite path to array to perform operations on
* @return array operations for the targeted array
*/
public ArrayOps opsForArray(String jsonPath) {
return opsForArray(JsonPath.compile(jsonPath));
}
/**
* Gets an {@link ArrayOps} for the array inside this JsonModel identified by the given JsonPath. The path must
* be definite ({@link com.jayway.jsonpath.JsonPath#isPathDefinite()}).
* <p/>
* Note that the element returned by the given path must be a json array.
*
* @param jsonPath definite path to array to perform operations on
* @return array operations for the targeted array
*/
public ArrayOps opsForArray(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
Object opsTarget = getTargetObject(jsonPath, List.class);
return new DefaultArrayOps(opsTarget);
return new DefaultArrayOps(jsonPath);
}
/**
* Gets an {@link ObjectOps} for this JsonModel. Note that the root element of this model
* must be a json object.
*
* @return object operations for this JsonModel
*/
public ObjectOps opsForObject() {
return new DefaultObjectOps(this.jsonObject);
return opsForObject(JSON_PATH_ROOT);
}
/**
* Gets an {@link ObjectOps} for the object inside this JsonModel identified by the given JsonPath. The path must
* be definite ({@link com.jayway.jsonpath.JsonPath#isPathDefinite()}).
* <p/>
* Note that the element returned by the given path must be a json object.
*
* @param jsonPath definite path to object to perform operations on
* @return object operations for the targeted object
*/
public ObjectOps opsForObject(String jsonPath) {
return opsForObject(JsonPath.compile(jsonPath));
}
/**
* Gets an {@link ObjectOps} for the object inside this JsonModel identified by the given JsonPath. The path must
* be definite ({@link com.jayway.jsonpath.JsonPath#isPathDefinite()}).
* <p/>
* Note that the element returned by the given path must be a json object.
*
* @param jsonPath definite path to object to perform operations on
* @return object operations for the targeted object
*/
public ObjectOps opsForObject(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
Object opsTarget = getTargetObject(jsonPath, Map.class);
return new DefaultObjectOps(opsTarget);
return new DefaultObjectOps(jsonPath);
}
// --------------------------------------------------------
@ -179,15 +313,31 @@ public class JsonModel {
// JSON extractors
//
// --------------------------------------------------------
public String getJson() {
/**
* Creates a JSON representation of this JsonModel
*
* @return model as Json
*/
public String toJson() {
return jsonProvider.toJson(jsonObject);
}
public String getJson(String jsonPath, Filter... filters) {
return getJson(JsonPath.compile(jsonPath, filters));
/**
* Creates a JSON representation of the result of the provided JsonPath
*
* @return path result as Json
*/
public String toJson(String jsonPath, Filter... filters) {
return toJson(JsonPath.compile(jsonPath, filters));
}
public String getJson(JsonPath jsonPath) {
/**
* Creates a JSON representation of the result of the provided JsonPath
*
* @return path result as Json
*/
public String toJson(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
return jsonProvider.toJson(get(jsonPath));
@ -199,19 +349,83 @@ public class JsonModel {
//
// --------------------------------------------------------
public JsonModel getModel(String jsonPath, Filter... filters) {
return getModel(JsonPath.compile(jsonPath, filters));
/**
* Returns a sub model from this JsonModel. A sub model can be any JSON object or JSON array
* addressed by a definite path. In contrast to a detached model changes on the sub model
* will be applied on the source model (the JsonModel from which the sub model was created)
*
*
* @param jsonPath the absolute path to extract a JsonModel for
* @return the new JsonModel
*
* @see com.jayway.jsonpath.JsonPath#isPathDefinite()
*/
public JsonModel getSubModel(String jsonPath) {
return getSubModel(JsonPath.compile(jsonPath));
}
public JsonModel getModel(JsonPath jsonPath) {
/**
* Returns a sub model from this JsonModel. A sub model can be any JSON object or JSON array
* addressed by a definite path. In contrast to a detached model changes on the sub model
* will be applied on the source model (the JsonModel from which the sub model was created)
*
*
* @param jsonPath the absolute path to extract a JsonModel for
* @return the new JsonModel
*
* @see com.jayway.jsonpath.JsonPath#isPathDefinite()
*/
public JsonModel getSubModel(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
isTrue(jsonPath.isPathDefinite(), "You can only get subModels with a definite path. Use getDetachedModel if path is not definite.");
Object subModel = jsonPath.read(jsonObject);
if(!jsonProvider.isContainer(subModel)){
if (!jsonProvider.isContainer(subModel)) {
throw new InvalidModelException("The path " + jsonPath.getPath() + " returned an invalid model " + (subModel != null ? subModel.getClass() : "null"));
}
return new JsonSubModel(subModel, this.jsonProvider, this, jsonPath);
}
// --------------------------------------------------------
//
// Detached sub model readers
//
// --------------------------------------------------------
/**
* Creates a detached sub model from this JsonModel. A detached sub model does not have
* to be created using a definite path. Changes on a detached sub model will not be reflected on the
* source model (the JsonModel from which the sub model was created).
*
* @param jsonPath the absolute path to extract a JsonModel for
* @param filters filters to expand the path
* @return a detached JsonModel
*/
public JsonModel getSubModelDetached(String jsonPath, Filter... filters) {
return getSubModelDetached(JsonPath.compile(jsonPath, filters));
}
/**
* Creates a detached sub model from this JsonModel. A detached sub model does not have
* to be created using a definite path. Changes on a detached sub model will not be reflected on the
* source model (the JsonModel from which the sub model was created).
*
* @param jsonPath the absolute path to extract a JsonModel for
* @return a detached JsonModel
*/
public JsonModel getSubModelDetached(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
Object subModel = jsonPath.read(jsonObject);
if (!jsonProvider.isContainer(subModel)) {
throw new InvalidModelException("The path " + jsonPath.getPath() + " returned an invalid model " + (subModel != null ? subModel.getClass() : "null"));
}
subModel = jsonProvider.clone(subModel);
return new JsonModel(subModel, this.jsonProvider);
}
@ -220,14 +434,33 @@ public class JsonModel {
// Mapping model readers
//
// --------------------------------------------------------
/**
* Returns a {@link MappingModelReader} for this JsonModel. Note that to use this functionality you need
* an optional dependencies on your classpath (jackson-mapper-asl ver >= 1.9.5)
*
* @return a object mapper
*/
public MappingModelReader map() {
return new DefaultMappingModelReader(this.jsonObject);
}
/**
* Returns a {@link MappingModelReader} for the JsonModel targeted by the provided {@link JsonPath}. Note that to use this functionality you need
* an optional dependencies on your classpath (jackson-mapper-asl ver >= 1.9.5)
*
* @return a object mapper
*/
public MappingModelReader map(String jsonPath, Filter... filters) {
return map(JsonPath.compile(jsonPath, filters));
}
/**
* Returns a {@link MappingModelReader} for the JsonModel targeted by the provided {@link JsonPath}. Note that to use this functionality you need
* an optional dependencies on your classpath (jackson-mapper-asl ver >= 1.9.5)
*
* @return a object mapper
*/
public MappingModelReader map(JsonPath jsonPath) {
notNull(jsonPath, "jsonPath can not be null");
@ -239,24 +472,49 @@ public class JsonModel {
// Static factory methods
//
// --------------------------------------------------------
/**
* Creates a JsonModel
*
* @param json json string
* @return a new JsonModel
*/
public static JsonModel model(String json) {
notEmpty(json, "json can not be null or empty");
return new JsonModel(json, JsonProviderFactory.createProvider());
}
/**
* Creates a JsonModel
*
* @param jsonObject a json container (a {@link Map} or a {@link List})
* @return a new JsonModel
*/
public static JsonModel model(Object jsonObject) {
notNull(jsonObject, "jsonObject can not be null");
return new JsonModel(jsonObject, JsonProviderFactory.createProvider());
}
/**
* Creates a JsonModel
*
* @param url pointing to a Json document
* @return a new JsonModel
*/
public static JsonModel model(URL url) throws IOException {
notNull(url, "url can not be null");
return new JsonModel(url, JsonProviderFactory.createProvider());
}
/**
* Creates a JsonModel
*
* @param jsonInputStream json document stream
* @return a new JsonModel
*/
public static JsonModel model(InputStream jsonInputStream) throws IOException {
notNull(jsonInputStream, "jsonInputStream can not be null");
@ -280,18 +538,50 @@ public class JsonModel {
Object modelRef = jsonObject;
LinkedList<PathToken> tokens = jsonPath.getTokenizer().getPathTokens();
if (jsonPath.getTokenizer().size() == 1) {
PathToken onlyToken = jsonPath.getTokenizer().iterator().next();
if ("$".equals(onlyToken.getFragment())) {
return clazz.cast(modelRef);
}
} else {
LinkedList<PathToken> tokens = jsonPath.getTokenizer().getPathTokens();
PathToken currentToken;
do {
currentToken = tokens.poll();
modelRef = currentToken.apply(modelRef, jsonProvider);
} while (!tokens.isEmpty());
if (modelRef.getClass().isAssignableFrom(clazz)) {
throw new InvalidModelException(jsonPath + " does nor refer to a Map but " + currentToken.getClass().getName());
}
return clazz.cast(modelRef);
}
throw new InvalidModelException();
}
private void setTargetObject(JsonPath jsonPath, Object newValue) {
JsonPath setterPath = jsonPath.copy();
PathToken pathToken = setterPath.getTokenizer().removeLastPathToken();
PathToken currentToken;
do {
currentToken = tokens.poll();
modelRef = currentToken.apply(modelRef, jsonProvider);
} while (!tokens.isEmpty());
if (modelRef.getClass().isAssignableFrom(clazz)) {
throw new InvalidModelException(jsonPath + " does nor refer to a Map but " + currentToken.getClass().getName());
if (pathToken.isRootToken()) {
if (this instanceof JsonSubModel) {
JsonSubModel thisModel = (JsonSubModel) this;
thisModel.parent.setTargetObject(thisModel.subModelPath, newValue);
} else {
this.jsonObject = newValue;
}
} else {
if (pathToken.isArrayIndexToken()) {
int arrayIndex = pathToken.getArrayIndex();
opsForArray(setterPath).set(arrayIndex, newValue);
} else {
opsForObject(setterPath).put(pathToken.getFragment(), newValue);
}
}
return clazz.cast(modelRef);
}
// --------------------------------------------------------
@ -300,178 +590,353 @@ public class JsonModel {
//
// --------------------------------------------------------
/**
* Converts a {@link JsonModel} to an Object
*/
public interface ObjectMappingModelReader {
/**
* Converts this JsonModel to the specified class using the configured {@link com.jayway.jsonpath.spi.MappingProvider}
*
* @see MappingProviderFactory
*
* @param targetClass class to convert the {@link JsonModel} to
* @param <T> template class
* @return the mapped model
*/
<T> T to(Class<T> targetClass);
}
/**
* Converts a {@link JsonModel} to an {@link Collection} of Objects
*/
public interface ListMappingModelReader {
/**
* Converts this JsonModel to the a list of objects with the provided class using the configured {@link com.jayway.jsonpath.spi.MappingProvider}
*
* @param targetClass class to convert the {@link JsonModel} array items to
* @param <T> template class
* @return the mapped mode
*/
<T> List<T> of(Class<T> targetClass);
/**
* Syntactic sugar function to use with {@link ListMappingModelReader#of}
*/
ListMappingModelReader toList();
/**
* Converts this JsonModel to the a {@link List} of objects with the provided class using the configured {@link com.jayway.jsonpath.spi.MappingProvider}
*
* @param targetClass class to convert the {@link JsonModel} array items to
* @param <T> template class
* @return the mapped mode
*/
<T> List<T> toListOf(Class<T> targetClass);
/**
* Converts this JsonModel to the a {@link Set} of objects with the provided class using the configured {@link com.jayway.jsonpath.spi.MappingProvider}
*
* @param targetClass class to convert the {@link JsonModel} array items to
* @param <T> template class
* @return the mapped model
*/
<T> Set<T> toSetOf(Class<T> targetClass);
}
/**
* Object mapping interface used when for root object that can be either a {@link List} or a {@link Map}.
* It's up to the invoker to know what the conversion target can be mapped to.
*/
public interface MappingModelReader extends ListMappingModelReader, ObjectMappingModelReader {
}
/**
* Operations that can be performed on Json objects ({@link Map}s)
*/
public interface ObjectOps {
/**
* Returns the operation target
* @return the operation target
*/
Map<String, Object> getTarget();
/**
* @see Map#containsKey(Object)
*/
boolean containsKey(String key);
/**
* @see Map#put(Object, Object)
*/
ObjectOps put(String key, Object value);
/**
* Adds the value to the target map if it is not already present
* @param key the key
* @param value the value
* @return this {@link ObjectOps}
*/
ObjectOps putIfAbsent(String key, Object value);
/**
* @see Map#get(Object)
*/
Object get(String key);
/**
* Tries to convert the value associated with the key to an {@link Integer}
* @param key the key
* @return converted value
*/
Integer getInteger(String key);
/**
* Tries to convert the value associated with the key to an {@link Long}
* @param key the key
* @return converted value
*/
Long getLong(String key);
/**
* Tries to convert the value associated with the key to an {@link Double}
* @param key the key
* @return converted value
*/
Double getDouble(String key);
/**
* @see Map#putAll(java.util.Map)
*/
ObjectOps putAll(Map<String, Object> map);
/**
* @see Map#remove(Object)
*/
ObjectOps remove(String key);
ObjectOps transform(Transformer<JsonModel> transformer);
/**
* Allows transformations of the target object. the target for this {@link ObjectOps} will be be replaced
* with the {@link Object} returned by the {@link Transformer#transform(Object)}
*
* @param transformer the transformer to use
* @return this {@link ObjectOps}
*/
ObjectOps transform(Transformer<Map<String, Object>> transformer);
/**
* Map the target of this {@link ObjectOps} to the provided class
* @param targetClass class to convert the target object to
* @param <T> template class
* @return the mapped model
*/
<T> T to(Class<T> targetClass);
}
/**
* Operations that can be performed on Json arrays ({@link List}s)
*/
public interface ArrayOps {
/**
* Returns the operation target
* @return the operation target
*/
List<Object> getTarget();
/**
* @see List#add(Object)
*/
ArrayOps add(Object o);
/**
* @see List#addAll(java.util.Collection)
*/
ArrayOps addAll(Collection<Object> collection);
/**
* @see List#remove(int)
*/
ArrayOps remove(Object o);
/**
* @see java.util.List#size()
*/
int size();
/**
* @see List#set(int, Object)
*/
ArrayOps set(int index, Object value);
/**
* Allows transformations of the target list. The target for this {@link ArrayOps} will be be replaced
* with the {@link Object} returned by the {@link Transformer#transform(Object)}
*
* @param transformer the transformer to use
* @return this {@link ArrayOps}
*/
ArrayOps transform(Transformer<List<Object>> transformer);
/**
* @see ListMappingModelReader
*/
ListMappingModelReader toList();
ArrayOps transform(Transformer<Object> transformer);
/**
* @see ListMappingModelReader
*/
<T> List<T> toListOf(Class<T> targetClass);
/**
* @see ListMappingModelReader
*/
<T> Set<T> toSetOf(Class<T> targetClass);
}
private static class DefaultObjectOps implements ObjectOps {
private class DefaultObjectOps implements ObjectOps {
private Map<String, Object> opsTarget;
private JsonPath jsonPath;
private DefaultObjectOps(Object opsTarget) {
this.opsTarget = (Map<String, Object>) opsTarget;
private DefaultObjectOps(JsonPath jsonPath) {
this.jsonPath = jsonPath;
}
@Override
public Map<String, Object> getTarget() {
return opsTarget;
return getTargetObject(jsonPath, Map.class);
}
@Override
public boolean containsKey(String key) {
return opsTarget.containsKey(key);
return getTargetObject(jsonPath, Map.class).containsKey(key);
}
@Override
public ObjectOps put(String key, Object value) {
opsTarget.put(key, value);
getTargetObject(jsonPath, Map.class).put(key, value);
return this;
}
@Override
public ObjectOps putIfAbsent(String key, Object value) {
if (!opsTarget.containsKey(key)) {
opsTarget.put(key, value);
Map targetObject = getTargetObject(jsonPath, Map.class);
if (!targetObject.containsKey(key)) {
targetObject.put(key, value);
}
return this;
}
@Override
public Object get(String key) {
return opsTarget.get(key);
return getTargetObject(jsonPath, Map.class).get(key);
}
@Override
public Integer getInteger(String key) {
return ConvertUtils.toInt(get(key));
}
@Override
public Long getLong(String key) {
return ConvertUtils.toLong(get(key));
}
@Override
public Double getDouble(String key) {
return ConvertUtils.toDouble(get(key));
}
@Override
public ObjectOps putAll(Map<String, Object> map) {
opsTarget.putAll(map);
getTargetObject(jsonPath, Map.class).putAll(map);
return this;
}
@Override
public ObjectOps remove(String key) {
opsTarget.remove(key);
getTargetObject(jsonPath, Map.class).remove(key);
return this;
}
@Override
public ObjectOps transform(Transformer<JsonModel> transformer) {
transformer.transform(-1, JsonModel.model(opsTarget));
public ObjectOps transform(Transformer<Map<String, Object>> transformer) {
Map targetObject = getTargetObject(jsonPath, Map.class);
Object transformed = transformer.transform(targetObject);
setTargetObject(jsonPath, transformed);
return this;
}
@Override
public <T> T to(Class<T> targetClass) {
return new DefaultMappingModelReader(opsTarget).to(targetClass);
Map targetObject = getTargetObject(jsonPath, Map.class);
return new DefaultMappingModelReader(targetObject).to(targetClass);
}
}
private static class DefaultArrayOps implements ArrayOps {
private List<Object> opsTarget;
private class DefaultArrayOps implements ArrayOps {
private JsonPath jsonPath;
private DefaultArrayOps(Object opsTarget) {
this.opsTarget = (List<Object>) opsTarget;
private DefaultArrayOps(JsonPath jsonPath) {
this.jsonPath = jsonPath;
}
@Override
public List<Object> getTarget() {
return opsTarget;
return getTargetObject(jsonPath, List.class);
}
@Override
public ArrayOps add(Object o) {
opsTarget.add(o);
getTargetObject(jsonPath, List.class).add(o);
return this;
}
@Override
public ArrayOps addAll(Collection<Object> collection) {
opsTarget.addAll(collection);
getTargetObject(jsonPath, List.class).addAll(collection);
return this;
}
@Override
public ArrayOps remove(Object o) {
opsTarget.remove(o);
getTargetObject(jsonPath, List.class).remove(o);
return this;
}
@Override
public int size() {
return getTargetObject(jsonPath, List.class).size();
}
@Override
public ArrayOps set(int index, Object value) {
getTargetObject(jsonPath, List.class).set(index, value);
return this;
}
@Override
public ListMappingModelReader toList() {
return new DefaultMappingModelReader(opsTarget);
return new DefaultMappingModelReader(getTargetObject(jsonPath, List.class));
}
@Override
public ArrayOps transform(Transformer<Object> transformer) {
for (int i = 0; i < opsTarget.size(); i++) {
Object current = opsTarget.get(i);
opsTarget.set(i, transformer.transform(i, current));
}
public ArrayOps transform(Transformer<List<Object>> transformer) {
Object transformed = transformer.transform(getTargetObject(jsonPath, List.class));
setTargetObject(jsonPath, transformed);
return this;
}
@Override
public <T> List<T> toListOf(Class<T> targetClass) {
return new DefaultMappingModelReader(opsTarget).toListOf(targetClass);
return new DefaultMappingModelReader(getTargetObject(jsonPath, List.class)).toListOf(targetClass);
}
@Override
public <T> Set<T> toSetOf(Class<T> targetClass) {
return new DefaultMappingModelReader(opsTarget).toSetOf(targetClass);
return new DefaultMappingModelReader(getTargetObject(jsonPath, List.class)).toSetOf(targetClass);
}
}
@ -517,4 +982,16 @@ public class JsonModel {
return MappingProviderFactory.createProvider().convertValue(model, targetClass);
}
}
private static class JsonSubModel extends JsonModel {
private final JsonModel parent;
private final JsonPath subModelPath;
private JsonSubModel(Object jsonObject, JsonProvider jsonProvider, JsonModel parent, JsonPath subModelPath) {
super(jsonObject, jsonProvider);
this.parent = parent;
this.subModelPath = subModelPath;
}
}
}

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

@ -17,11 +17,12 @@ package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.PathToken;
import com.jayway.jsonpath.internal.PathTokenizer;
import com.jayway.jsonpath.internal.Util;
import com.jayway.jsonpath.internal.IOUtils;
import com.jayway.jsonpath.internal.filter.PathTokenFilter;
import com.jayway.jsonpath.spi.HttpProviderFactory;
import com.jayway.jsonpath.spi.JsonProvider;
import com.jayway.jsonpath.spi.JsonProviderFactory;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.net.URL;
@ -108,8 +109,11 @@ public class JsonPath {
throw new InvalidPathException("Invalid path");
}
int filterCountInPath = StringUtils.countMatches(jsonPath, "[?]");
isTrue(filterCountInPath == filters.length, "Filters in path ([?]) does not match provided filters.");
this.tokenizer = new PathTokenizer(jsonPath);
this.filters = new LinkedList<Filter>();
this.filters.addAll(asList(filters));
@ -120,6 +124,10 @@ public class JsonPath {
}
public JsonPath copy(){
return new JsonPath(tokenizer.getPath(), filters.toArray(new Filter[0]));
}
/**
* Returns the string representation of this JsonPath
@ -220,7 +228,7 @@ public class JsonPath {
in = HttpProviderFactory.getProvider().get(jsonURL);
return (T) read(JsonProviderFactory.createProvider().parse(in));
} finally {
Util.closeQuietly(in);
IOUtils.closeQuietly(in);
}
}
@ -242,7 +250,7 @@ public class JsonPath {
fis = new FileInputStream(jsonFile);
return (T) read(JsonProviderFactory.createProvider().parse(fis));
} finally {
Util.closeQuietly(fis);
IOUtils.closeQuietly(fis);
}
}
@ -261,7 +269,7 @@ public class JsonPath {
try {
return (T) read(JsonProviderFactory.createProvider().parse(jsonInputStream));
} finally {
Util.closeQuietly(jsonInputStream);
IOUtils.closeQuietly(jsonInputStream);
}
}

30
json-path/src/main/java/com/jayway/jsonpath/Transformer.java

@ -1,13 +1,31 @@
/*
* 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;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
* Date: 3/9/12
* Time: 3:20 PM
* @author Kalle Stenflo
*/
public interface Transformer {
public interface Transformer<T> {
/**
*
* @param obj object to transform
* @return the transformed object
*/
public Object transform(T obj);
public JsonModel transform(JsonModel model);
}

78
json-path/src/main/java/com/jayway/jsonpath/internal/ConvertUtils.java

@ -0,0 +1,78 @@
/*
* 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;
import com.jayway.jsonpath.InvalidConversionException;
/**
* @author Kalle Stenflo
*/
public class ConvertUtils {
/**
* converts to Integer with radix 10
*
* @param o object to convert
* @return converted value
*/
public static Integer toInt(Object o) {
if (null == o)
return null;
if (o instanceof Number)
return ((Number) o).intValue();
try {
return Integer.valueOf(o.toString().trim(), 10);
} catch (Exception e) {
throw new InvalidConversionException("Could not convert " + o.toString() + " to Integer");
}
}
/**
* converts to Long with radix 10
*
* @param o object to convert
* @return converted value
*/
public static Long toLong(Object o) {
if (null == o)
return null;
if (o instanceof Number)
return ((Number) o).longValue();
try {
return Long.valueOf(o.toString().trim(), 10);
} catch (Exception e) {
throw new InvalidConversionException("Could not convert " + o.toString() + " to Long");
}
}
/**
* converts to Double with radix 10
*
* @param o object to convert
* @return converted value
*/
public static Double toDouble(Object o) {
if (null == o)
return null;
if (o instanceof Number)
return ((Number) o).doubleValue();
try {
return Double.valueOf(o.toString().trim());
} catch (Exception e) {
throw new InvalidConversionException("Could not convert " + o.toString() + " to Double");
}
}
}

32
json-path/src/main/java/com/jayway/jsonpath/internal/IOUtils.java

@ -0,0 +1,32 @@
/*
* 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;
import java.io.Closeable;
import java.io.IOException;
/**
* @author Kalle Stenflo
*/
public abstract class IOUtils {
public static void closeQuietly(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException ignore) {}
}
}

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

@ -14,14 +14,20 @@
*/
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.InvalidModelException;
import com.jayway.jsonpath.internal.filter.FilterFactory;
import com.jayway.jsonpath.internal.filter.PathTokenFilter;
import com.jayway.jsonpath.spi.JsonProvider;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Kalle Stenflo
*/
public class PathToken {
private static final Pattern ARRAY_INDEX_PATTERN = Pattern.compile("\\[(\\d+)\\]");
private String fragment;
@ -44,4 +50,19 @@ public class PathToken {
public String getFragment() {
return fragment;
}
public boolean isRootToken(){
return "$".equals(fragment);
}
public boolean isArrayIndexToken(){
return ARRAY_INDEX_PATTERN.matcher(fragment).matches();
}
public int getArrayIndex(){
Matcher matcher = ARRAY_INDEX_PATTERN.matcher(fragment);
if(matcher.find()){
return Integer.parseInt(matcher.group(1));
}
else throw new InvalidModelException("Could not get array index from fragment " + fragment);
}
}

12
json-path/src/main/java/com/jayway/jsonpath/internal/PathTokenizer.java

@ -50,6 +50,10 @@ public class PathTokenizer implements Iterable<PathToken> {
return fragments;
}
public int size(){
return pathTokens.size();
}
public String getPath() {
return new String(pathChars);
}
@ -62,6 +66,13 @@ public class PathTokenizer implements Iterable<PathToken> {
return pathTokens.iterator();
}
public PathToken removeLastPathToken(){
PathToken lastPathToken = pathTokens.get(pathTokens.size() - 1);
//TODO: this should also trim the pathChars
pathTokens.remove(pathTokens.size() - 1);
return lastPathToken;
}
//--------------------------------------------
//
@ -111,7 +122,6 @@ public class PathTokenizer implements Iterable<PathToken> {
default:
fragments.add(extract(false, '[', '.'));
}
}
return fragments;

10
json-path/src/main/java/com/jayway/jsonpath/internal/Util.java

@ -1,10 +0,0 @@
package com.jayway.jsonpath.internal;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
* Date: 3/10/12
* Time: 7:25 AM
*/
public class Util {
}

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

@ -54,7 +54,7 @@ public class ArrayIndexFilter extends PathTokenFilter {
return result;
} else if (trimmedCondition.endsWith(":")) {
trimmedCondition = trim(trimmedCondition, 1, 1);
trimmedCondition = trim(trimmedCondition.replace(" ", ""), 1, 1);
int get = Integer.parseInt(trimmedCondition);
return src.get(src.size() - get);

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

@ -1,5 +1,9 @@
package com.jayway.jsonpath.spi;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
@ -7,4 +11,7 @@ package com.jayway.jsonpath.spi;
* Time: 7:38 AM
*/
public interface HttpProvider {
InputStream get(URL url) throws IOException;
}

28
json-path/src/main/java/com/jayway/jsonpath/spi/HttpProviderFactory.java

@ -1,10 +1,36 @@
package com.jayway.jsonpath.spi;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
* Date: 3/10/12
* Time: 7:39 AM
*/
public class HttpProviderFactory {
public abstract class HttpProviderFactory {
public static HttpProviderFactory factory = new HttpProviderFactory() {
@Override
protected HttpProvider create() {
return new HttpProvider() {
@Override
public InputStream get(URL url) throws IOException {
URLConnection connection = url.openConnection();
connection.setRequestProperty("Accept", "application/json");
return connection.getInputStream();
}
};
}
};
public static HttpProvider getProvider(){
return factory.create();
}
protected abstract HttpProvider create();
}

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

@ -41,6 +41,8 @@ public interface JsonProvider {
List<Object> createList();
Object clone(Object model);
/**
* checks if object is <code>instanceof</code> <code>java.util.List</code> or <code>java.util.Map</code>

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

@ -25,6 +25,7 @@ public abstract class JsonProviderFactory {
@Override
protected JsonProvider create() {
return new JsonSmartJsonProvider();
//return new JacksonProvider();
}
};

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

@ -15,7 +15,9 @@
package com.jayway.jsonpath.spi.impl;
import com.jayway.jsonpath.spi.JsonProvider;
import org.apache.commons.lang.SerializationUtils;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@ -24,6 +26,11 @@ import java.util.Map;
*/
public abstract class AbstractJsonProvider implements JsonProvider {
@Override
public Object clone(Object obj){
return SerializationUtils.clone((Serializable)obj);
}
/**
* checks if object is <code>instanceof</code> <code>java.util.List</code> or <code>java.util.Map</code>
*

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

@ -17,12 +17,14 @@ package com.jayway.jsonpath.spi.impl;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.spi.MappingProvider;
import com.jayway.jsonpath.spi.Mode;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.CollectionType;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.util.*;
/**
@ -66,7 +68,15 @@ public class JacksonProvider extends AbstractJsonProvider implements MappingProv
@Override
public String toJson(Object obj) {
throw new UnsupportedOperationException();
StringWriter writer = new StringWriter();
try {
JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(writer);
objectMapper.writeValue(jsonGenerator, obj);
writer.close();
return writer.getBuffer().toString();
} catch (IOException e) {
throw new InvalidJsonException();
}
}
@Override

30
json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java

@ -17,8 +17,8 @@ public class ComplianceTest {
@Test
public void test_one() throws Exception {
String json = "{ a: \"a\",\n" +
" b: \"b\",\n" +
String json = "{ \"a\": \"a\",\n" +
" \"b\": \"b\",\n" +
" \"c d\": \"e\" \n" +
" }";
@ -39,23 +39,23 @@ public class ComplianceTest {
assertThat(JsonPath.<Integer>read(json, "$[0]"), is(equalTo(1)));
assertThat(JsonPath.<Integer>read(json, "$[4]"), is(equalTo(null)));
assertThat(JsonPath.<List<Comparable>>read(json, "$[*]"), hasItems(
(Comparable)new Integer(1),
(Comparable)new String("2"),
(Comparable)new Double(3.14),
(Comparable)new Boolean(true),
null));
new Integer(1),
new String("2"),
new Double(3.14),
new Boolean(true),
(Comparable)null));
assertThat(JsonPath.<Boolean>read(json, "$[-1:]"), is(equalTo(null)));
}
@Test
public void test_three() throws Exception {
String json = "{ points: [\n" +
" { id: \"i1\", x: 4, y: -5 },\n" +
" { id: \"i2\", x: -2, y: 2, z: 1 },\n" +
" { id: \"i3\", x: 8, y: 3 },\n" +
" { id: \"i4\", x: -6, y: -1 },\n" +
" { id: \"i5\", x: 0, y: 2, z: 1 },\n" +
" { id: \"i6\", x: 1, y: 4 }\n" +
String json = "{ \"points\": [\n" +
" { \"id\": \"i1\", \"x\": 4, \"y\": -5 },\n" +
" { \"id\": \"i2\", \"x\": -2, \"y\": 2, \"z\": 1 },\n" +
" { \"id\": \"i3\", \"x\": 8, \"y\": 3 },\n" +
" { \"id\": \"i4\", \"x\": -6, \"y\": -1 },\n" +
" { \"id\": \"i5\", \"x\": 0, \"y\": 2, \"z\": 1 },\n" +
" { \"id\": \"i6\", \"x\": 1, \"y\": 4 }\n" +
" ]\n" +
" }";
@ -74,7 +74,7 @@ public class ComplianceTest {
assertThat(JsonPath.<List<String>>read(json, "$.points[?(@.z)].id"), hasItems("i2", "i5"));
assertThat(JsonPath.<String>read(json, "$.points[(@.length-1)].id"), equalTo("i6"));
assertThat(JsonPath.<String>read(json, "$.points[(@.length - 1)].id"), equalTo("i6"));
//assertThat(JsonPath.<List<Integer>>read(json, "$['points'][?(@.x * @.x + @.y * @.y > 50)].id"), hasItems(?)); //low
}

38
json-path/src/test/java/com/jayway/jsonpath/HttpProviderTest.java

@ -1,5 +1,15 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.IOUtils;
import com.jayway.jsonpath.spi.HttpProviderFactory;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import static junit.framework.Assert.assertEquals;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
@ -7,4 +17,32 @@ package com.jayway.jsonpath;
* Time: 8:12 AM
*/
public class HttpProviderTest {
private static final String EXPECTED = "{\n" +
" \"results\" : [],\n" +
" \"status\" : \"REQUEST_DENIED\"\n" +
"}";
@Test
public void http_get() throws Exception {
URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json");
InputStream inputStream = null;
try {
inputStream = HttpProviderFactory.getProvider().get(url);
byte[] bytes = sun.misc.IOUtils.readFully(inputStream, -1, true);
String json = new String(bytes).trim();
assertEquals(EXPECTED, json);
} catch (IOException e) {
IOUtils.closeQuietly(inputStream);
}
}
}

145
json-path/src/test/java/com/jayway/jsonpath/JsonModelOpsTest.java

@ -2,11 +2,9 @@ package com.jayway.jsonpath;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static com.jayway.jsonpath.JsonModel.model;
import static junit.framework.Assert.assertEquals;
/**
@ -47,11 +45,28 @@ public class JsonModelOpsTest {
" \"color\": \"red\",\n" +
" \"price\": 19.95,\n" +
" \"foo:bar\": \"fooBar\",\n" +
" \"number\": 12,\n" +
" \"dot.notation\": \"new\"\n" +
" }\n" +
" }\n" +
"}";
@Test
public void convert_values() throws Exception {
JsonModel model = model(DOCUMENT).getSubModel("store.bicycle");
JsonModel.ObjectOps ops = model.opsForObject();
assertEquals(19.95D, ops.getDouble("price"));
assertEquals(new Long(12), ops.getLong("number"));
assertEquals(new Integer(12), ops.getInteger("number"));
int i = ops.getInteger("number");
long l = ops.getLong("number");
double d = ops.getDouble("price");
}
@Test
public void object_ops_can_update() throws Exception {
@ -64,12 +79,12 @@ public class JsonModelOpsTest {
assertEquals("Kalle", model.get("store.book[0].author"));
assertEquals(12.30D, model.get("store.book[0].price"));
}
@Test
public void array_ops_can_add_element() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
Map<String, Object> newBook = new HashMap<String, Object>();
newBook.put("category", "reference");
newBook.put("author", "Kalle");
@ -79,7 +94,7 @@ public class JsonModelOpsTest {
model.opsForArray("store.book").add(newBook);
JsonModel subModel = model.getModel("store.book[4]");
JsonModel subModel = model.getSubModel("store.book[4]");
assertEquals("reference", subModel.get("category"));
assertEquals("Kalle", subModel.get("author"));
@ -88,6 +103,75 @@ public class JsonModelOpsTest {
assertEquals(12.10D, subModel.get("price"));
}
@Test
public void ops_can_transform_object_root() throws Exception {
Map<String, Object> rootDocument = new HashMap<String, Object>();
rootDocument.put("category", "reference");
rootDocument.put("author", "Kalle");
rootDocument.put("title", "JSONPath book");
rootDocument.put("isbn", "0-553-21311-34");
rootDocument.put("price", 12.10D);
JsonModel model = JsonModel.model(rootDocument);
model.opsForObject().transform(new Transformer<Map<String, Object>>() {
@Override
public Object transform(Map<String, Object> obj) {
obj.put("name", "kalle");
return obj;
}
});
assertEquals("kalle", model.get("name"));
}
@Test
public void ops_can_transform_array_root() throws Exception {
List<Object> rootDocument = new ArrayList<Object>();
rootDocument.add(Collections.singletonMap("name", "kalle"));
rootDocument.add(Collections.singletonMap("name", "bob"));
rootDocument.add(Collections.singletonMap("name", "zak"));
JsonModel model = JsonModel.model(rootDocument);
model.opsForArray().transform(new Transformer<List<Object>>() {
@Override
public Object transform(List<Object> obj) {
return Collections.singletonMap("root", "new");
}
});
assertEquals("new", model.get("root"));
}
@Test
public void ops_can_transform_nested_document() throws Exception {
Map<String, Object> childDocument = new HashMap<String, Object>();
childDocument.put("level", 1);
Map<String, Object> rootDocument = new HashMap<String, Object>();
rootDocument.put("category", "reference");
rootDocument.put("author", "Kalle");
rootDocument.put("title", "JSONPath book");
rootDocument.put("isbn", "0-553-21311-34");
rootDocument.put("price", 12.10D);
rootDocument.put("child", childDocument);
JsonModel model = JsonModel.model(rootDocument);
model.opsForObject("child").transform(new Transformer<Map<String, Object>>() {
@Override
public Object transform(Map<String, Object> obj) {
obj.put("name", "kalle");
return obj;
}
});
assertEquals("kalle", model.get("child.name"));
}
@Test
public void arrays_can_be_mapped() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
@ -118,10 +202,10 @@ public class JsonModelOpsTest {
@Test
public void object_can_be_transformed() throws Exception {
Transformer transformer = new Transformer<JsonModel>() {
Transformer transformer = new Transformer<Map<String, Object>>() {
@Override
public JsonModel transform(int index, JsonModel model) {
model.opsForObject().put("newProp", "newProp");
public Map<String, Object> transform(Map<String, Object> model) {
model.put("newProp", "newProp");
return model;
}
};
@ -135,11 +219,15 @@ public class JsonModelOpsTest {
@Test
public void arrays_can_be_transformed() throws Exception {
Transformer transformer = new Transformer<Object>() {
Transformer transformer = new Transformer<List<Object>>() {
@Override
public Object transform(int index, Object model) {
Map<String, Object> map = (Map<String, Object>) model;
map.put("newProp", "newProp");
public Object transform(List<Object> model) {
for (Object o : model) {
Map<String, Object> map = (Map<String, Object>) o;
map.put("newProp", "newProp");
}
return model;
}
};
@ -150,26 +238,37 @@ public class JsonModelOpsTest {
assertEquals("newProp", model.get("store.book[1].newProp"));
}
@Test
public void array_can_be_transformed_to_primitives() throws Exception {
Transformer positionTransformer = new Transformer<Object>() {
Transformer positionTransformer = new Transformer<List<Object>>() {
private int i = 0;
@Override
public Object transform(int index, Object model) {
return i++;
public Object transform(List<Object> model) {
List<Object> newList = new ArrayList<Object>();
for (Object o : model) {
newList.add(new Integer(i++));
}
return newList;
}
};
Transformer multiplyingTransformer = new Transformer<Object>() {
Transformer multiplyingTransformer = new Transformer<List<Object>>() {
@Override
public Object transform(int index, Object model) {
int in = (Integer)model;
public Object transform(List<Object> model) {
return in * 2;
for (int i = 0; i < model.size(); i++) {
int curr = (Integer) model.get(i);
model.set(i, curr * 2);
}
return model;
}
};
JsonModel model = JsonModel.model(DOCUMENT);
model.opsForArray("store.book").transform(positionTransformer).transform(multiplyingTransformer);

65
json-path/src/test/java/com/jayway/jsonpath/JsonModelSubModelDetachedTest.java

@ -0,0 +1,65 @@
package com.jayway.jsonpath;
import org.junit.Test;
import static com.jayway.jsonpath.JsonModel.model;
import static junit.framework.Assert.assertEquals;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
* Date: 3/11/12
* Time: 4:44 PM
*/
public class JsonModelSubModelDetachedTest {
public final static String DOCUMENT =
"{ \"store\": {\n" +
" \"book\": [ \n" +
" { \"category\": \"reference\",\n" +
" \"author\": \"Nigel Rees\",\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" +
" },\n" +
" { \"category\": \"fiction\",\n" +
" \"author\": \"Herman Melville\",\n" +
" \"title\": \"Moby Dick\",\n" +
" \"isbn\": \"0-553-21311-3\",\n" +
" \"price\": 8.99\n" +
" },\n" +
" { \"category\": \"fiction\",\n" +
" \"author\": \"J. R. R. Tolkien\",\n" +
" \"title\": \"The Lord of the Rings\",\n" +
" \"isbn\": \"0-395-19395-8\",\n" +
" \"price\": 22.99\n" +
" }\n" +
" ],\n" +
" \"bicycle\": {\n" +
" \"color\": \"red\",\n" +
" \"price\": 19.95,\n" +
" \"foo:bar\": \"fooBar\",\n" +
" \"dot.notation\": \"new\"\n" +
" }\n" +
" }\n" +
"}";
@Test
public void a_sub_model_can_be_detached() throws Exception {
JsonModel model = model(DOCUMENT);
JsonModel detachedModel = model.getSubModelDetached("$.store.book[*]");
detachedModel.opsForArray().add(1);
assertEquals(4, model.opsForArray("$.store.book").size());
assertEquals(5, detachedModel.opsForArray("$.store.book").size());
}
}

116
json-path/src/test/java/com/jayway/jsonpath/JsonModelSubModelTest.java

@ -0,0 +1,116 @@
package com.jayway.jsonpath;
import org.junit.Test;
import java.util.Collections;
import java.util.Map;
import static junit.framework.Assert.assertEquals;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
* Date: 3/11/12
* Time: 5:07 PM
*/
public class JsonModelSubModelTest {
public final static String DOCUMENT =
"{ \"store\": {\n" +
" \"book\": [ \n" +
" { \"category\": \"reference\",\n" +
" \"author\": \"Nigel Rees\",\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" +
" },\n" +
" { \"category\": \"fiction\",\n" +
" \"author\": \"Herman Melville\",\n" +
" \"title\": \"Moby Dick\",\n" +
" \"isbn\": \"0-553-21311-3\",\n" +
" \"price\": 8.99\n" +
" },\n" +
" { \"category\": \"fiction\",\n" +
" \"author\": \"J. R. R. Tolkien\",\n" +
" \"title\": \"The Lord of the Rings\",\n" +
" \"isbn\": \"0-395-19395-8\",\n" +
" \"price\": 22.99\n" +
" }\n" +
" ],\n" +
" \"bicycle\": {\n" +
" \"color\": \"red\",\n" +
" \"price\": 19.95,\n" +
" \"foo:bar\": \"fooBar\",\n" +
" \"dot.notation\": \"new\",\n" +
" \"book\": { \"category\": \"fiction\",\n" +
" \"author\": \"J. R. R. Tolkien\",\n" +
" \"title\": \"The Lord of the Rings\",\n" +
" \"isbn\": \"0-395-19395-8\",\n" +
" \"price\": 22.99\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
@Test(expected = IllegalArgumentException.class)
public void a_sub_model_path_must_be_definite() throws Exception {
JsonModel.model(DOCUMENT).getSubModel("$store.book[*]");
}
@Test
public void test_a_sub_model_can_be_fetched_and_read() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
assertEquals("Nigel Rees", model.getSubModel("$store.book[0]").get("author"));
assertEquals("Nigel Rees", model.getSubModel(JsonPath.compile("$store.book[0]")).get("author"));
}
@Test
public void when_a_sub_model_is_updated_the_master_model_is_updated() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
JsonModel subModel = model.getSubModel("store.book[0]");
subModel.opsForObject().put("author", "kalle");
assertEquals("kalle", model.get("store.book[0].author"));
}
@Test
public void when_a_sub_model_root_is_transformed_the_master_model_is_updated() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
JsonModel subModel = model.getSubModel("store.book[0]");
subModel.opsForObject().transform(new Transformer<Map<String, Object>>() {
@Override
public Object transform(Map<String, Object> obj) {
return Collections.singletonMap("prop", "new");
}
});
assertEquals("new", model.get("store.book[0].prop"));
}
@Test
public void when_a_sub_model_child_is_transformed_the_master_model_is_updated() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
JsonModel subModel = model.getSubModel("store.bicycle.book");
subModel.opsForObject().transform(new Transformer<Map<String, Object>>() {
@Override
public Object transform(Map<String, Object> obj) {
return Collections.singletonMap("prop", "new");
}
});
assertEquals("new", model.get("store.bicycle.book.prop"));
}
}

21
json-path/src/test/java/com/jayway/jsonpath/JsonModelTest.java

@ -10,6 +10,8 @@ import java.util.Map;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
/**
* Created by IntelliJ IDEA.
@ -54,6 +56,16 @@ public class JsonModelTest {
" }\n" +
"}";
@Test
public void has_path_validates() throws Exception {
assertFalse(JsonModel.model(DOCUMENT).hasPath("store.invalid"));
assertFalse( JsonModel.model(DOCUMENT).hasPath("store.book[0].foo"));
assertTrue( JsonModel.model(DOCUMENT).hasPath("store.book"));
assertTrue( JsonModel.model(DOCUMENT).hasPath("store.book[0].title"));
}
@Test
public void a_json_document_can_be_fetched_with_a_URL() throws Exception {
URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json");
@ -66,12 +78,7 @@ public class JsonModelTest {
assertEquals("Nigel Rees", JsonModel.model(bis).get("store.book[0].author"));
}
@Test
public void test_a_sub_model_can_be_fetched_and_read() throws Exception {
JsonModel model = JsonModel.model(DOCUMENT);
assertEquals("Nigel Rees", model.getModel("$store.book[0]").get("author"));
assertEquals("Nigel Rees", model.getModel(JsonPath.compile("$store.book[0]")).get("author"));
}
@Test
public void maps_and_list_can_queried() throws Exception {
@ -83,7 +90,7 @@ public class JsonModelTest {
assertEquals("value", model.get("$child.key"));
assertEquals(1, model.get("$items[1]"));
assertEquals("{\"child\":{\"key\":\"value\"},\"items\":[0,1,2]}", model.getJson());
assertEquals("{\"child\":{\"key\":\"value\"},\"items\":[0,1,2]}", model.toJson());
}

20
json-path/src/test/java/com/jayway/jsonpath/JsonProviderTest.java

@ -1,8 +1,13 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.spi.impl.JacksonProvider;
import org.apache.commons.lang.SerializationUtils;
import org.junit.Test;
import java.io.Serializable;
import static com.jayway.jsonpath.JsonModel.model;
/**
* Created by IntelliJ IDEA.
* User: kallestenflo
@ -49,6 +54,19 @@ public class JsonProviderTest {
"}";
@Test
public void clone_test() throws Exception {
Serializable jsonObject = (Serializable) model(DOCUMENT).getJsonObject();
Object clone = SerializationUtils.clone(jsonObject);
System.out.println(model(clone).toJson());
}
@Test
public void parse_document() throws Exception {
@ -62,7 +80,7 @@ public class JsonProviderTest {
@Test
public void parse_array() throws Exception {
JacksonProvider provider = new JacksonProvider();
JacksonProvider provider = new JacksonProvider();
Object o = provider.parse(ARRAY);

13
json-path/src/test/java/com/jayway/jsonpath/PathTest.java

@ -15,11 +15,22 @@ import static org.junit.Assert.*;
*/
public class PathTest {
Filter filter = new Filter(){
@Override
public boolean accept(Object obj) {
return true;
}
@Override
public Filter addCriteria(Criteria criteria) {
return this;
}
};
@Test
public void path_is_not_definite() throws Exception {
assertFalse(JsonPath.compile("$..book[0]").isPathDefinite());
assertFalse(JsonPath.compile("$book[?]").isPathDefinite());
assertFalse(JsonPath.compile("$book[?]", filter).isPathDefinite());
assertFalse(JsonPath.compile("$.books[*]").isPathDefinite());
}

Loading…
Cancel
Save