com.jayway.restassured.path.json.JsonPath Maven / Gradle / Ivy
/*
* 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.restassured.path.json;
import com.jayway.restassured.assertion.JSONAssertion;
import com.jayway.restassured.exception.ParsePathException;
import com.jayway.restassured.internal.mapping.ObjectMapping;
import com.jayway.restassured.internal.support.Prettifier;
import com.jayway.restassured.parsing.Parser;
import groovy.json.JsonBuilder;
import groovy.json.JsonOutput;
import groovy.json.JsonSlurper;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import static com.jayway.restassured.assertion.AssertParameter.notNull;
import static com.jayway.restassured.internal.path.ObjectConverter.convertObjectTo;
/**
* JsonPath is an alternative to using XPath for easily getting values from a Object document. It follows the
* Groovy dot notation syntax when getting an object from the document. You can regard it as an alternative to XPath for XML.
* E.g. given the following Object document:
*
* { "store": {
* "book": [
* { "category": "reference",
* "author": "Nigel Rees",
* "title": "Sayings of the Century",
* "price": 8.95
* },
* { "category": "fiction",
* "author": "Evelyn Waugh",
* "title": "Sword of Honour",
* "price": 12.99
* },
* { "category": "fiction",
* "author": "Herman Melville",
* "title": "Moby Dick",
* "isbn": "0-553-21311-3",
* "price": 8.99
* },
* { "category": "fiction",
* "author": "J. R. R. Tolkien",
* "title": "The Lord of the Rings",
* "isbn": "0-395-19395-8",
* "price": 22.99
* }
* ],
* "bicycle": {
* "color": "red",
* "price": 19.95
* }
* }
* }
*
* To get a list of all book categories:
*
* List<String> categories = with(Object).get("store.book.category");
*
*
* Get the first book category:
*
* String category = with(Object).get("store.book[0].category");
*
*
* Get the last book category:
*
* String category = with(Object).get("store.book[-1].category");
*
*
* Get all books with price between 5 and 15:
*
* List<Map> books = with(Object).get("store.book.findAll { book -> book.price >= 5 && book.price <= 15 }");
*
*
*/
public class JsonPath {
private final Object json;
private String rootPath = "";
/**
* Instantiate a new JsonPath instance.
*
* @param text The text containing the Object document
*/
public JsonPath(String text) {
json = new JsonSlurper().parseText(text);
}
/**
* Instantiate a new JsonPath instance.
*
* @param url The url containing the Object document
*/
public JsonPath(URL url) {
json = parseURL(url);
}
/**
* Instantiate a new JsonPath instance.
*
* @param stream The stream containing the Object document
*/
public JsonPath(InputStream stream) {
json = parseInputStream(stream);
}
/**
* Instantiate a new JsonPath instance.
*
* @param file The file containing the Object document
*/
public JsonPath(File file) {
json = parseFile(file);
}
/**
* Instantiate a new JsonPath instance.
*
* @param reader The reader containing the Object document
*/
public JsonPath(Reader reader) {
json = parseReader(reader);
}
/**
* Get a Object graph with no named root element as a Java object. This is just a short-cut for
*
*
* get("");
*
* or
*
* get("$");
*
*
* @return The object matching the Object graph. This may be any primitive type, a List or a Map. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public T get() {
return get("");
}
/**
* Get the result of an Object path expression as a boolean.
*
* @param path The Object path.
* @return The object matching the Object path. This may be any primitive type, a List or a Map. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public T get(String path) {
final JSONAssertion jsonAssertion = createJsonAssertion(path);
return (T) jsonAssertion.getResult(json);
}
/**
* Get the result of an Object path expression as a boolean
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public boolean getBoolean(String path) {
return convertObjectTo(get(path), Boolean.class);
}
/**
* Get the result of an Object path expression as a char.
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public char getChar(String path) {
return convertObjectTo(get(path), Character.class);
}
/**
* Get the result of an Object path expression as an int.
*
* @param path The Object path.
* @return The int matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public int getInt(String path) {
//The type returned from Groovy depends on the input, so we need to handle different numerical types.
Object value = get(path);
if(value instanceof Integer) {
return (Integer) value;
}else if (value instanceof Short) {
return ((Short)value).intValue();
} else if (value instanceof Long) {
return ((Long)value).intValue();
} else {
return convertObjectTo(value, Integer.class);
}
}
/**
* Get the result of an Object path expression as a byte.
*
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public byte getByte(String path) {
//The type returned from Groovy depends on the input, so we need to handle different numerical types.
Object value = get(path);
if(value instanceof Byte) {
return (Byte) value;
} else if (value instanceof Long) {
return ((Long)value).byteValue();
} else if (value instanceof Integer) {
return ((Integer)value).byteValue();
} else {
return convertObjectTo(value, Byte.class);
}
}
/**
* Get the result of an Object path expression as a short.
*
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public short getShort(String path) {
//The type returned from Groovy depends on the input, so we need to handle different numerical types.
Object value = get(path);
if(value instanceof Short) {
return (Short) value;
} else if (value instanceof Long) {
return ((Long)value).shortValue();
} else if (value instanceof Integer) {
return ((Integer)value).shortValue();
} else {
return convertObjectTo(value, Short.class);
}
}
/**
* Get the result of an Object path expression as a float.
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public float getFloat(String path) {
final Object value = get(path);
//Groovy will always return a Double for floating point values.
if(value instanceof Double) {
return ((Double) value).floatValue();
} else {
return convertObjectTo(value, Float.class);
}
}
/**
* Get the result of an Object path expression as a double.
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public double getDouble(String path) {
final Object value = get(path);
if(value instanceof Double) {
return (Double) value;
}
return convertObjectTo(value, Double.class);
}
/**
* Get the result of an Object path expression as a long.
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public long getLong(String path) {
//The type returned from Groovy depends on the input, so we need to handle different numerical types.
Object value = get(path);
if(value instanceof Long) {
return (Long) value;
} else if (value instanceof Short) {
return ((Short)value).longValue();
} else if (value instanceof Integer) {
return ((Integer)value).longValue();
} else {
return convertObjectTo(value, Long.class);
}
}
/**
* Get the result of an Object path expression as a string.
*
* @param path The Object path.
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public String getString(String path) {
return convertObjectTo(get(path), String.class);
}
/**
* Get the result of an Object path expression as a list.
*
* @param path The Object path.
* @param The list type
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public List getList(String path) {
return get(path);
}
/**
* Get the result of an Object path expression as a list.
*
* @param path The Object path.
* @param genericType The generic list type
* @param The type
* @return The object matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public List getList(String path, Class genericType) {
final List original = get(path);
final List newList = new LinkedList();
for (T t : original) {
newList.add(convertObjectTo(t, genericType));
}
return Collections.unmodifiableList(newList);
}
/**
* Get the result of an Object path expression as a map.
*
* @param path The Object path.
* @param The type of the expected key
* @param The type of the expected value
* @return The map matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public Map getMap(String path) {
return get(path);
}
/**
* Get the result of an Object path expression as a map.
*
* @param path The Object path.
* @param keyType The type of the expected key
* @param valueType The type of the expected value
* @param The type of the expected key
* @param The type of the expected value
* @return The map matching the Object path. A {@java.lang.ClassCastException} will be thrown if the object
* cannot be casted to the expected type.
*/
public Map getMap(String path, Class keyType, Class valueType) {
final Map originalMap = get(path);
final Map newMap = new HashMap();
for (Entry entry : originalMap.entrySet()) {
final K key = entry.getKey() == null ? null : convertObjectTo(entry.getKey(), keyType);
final V value = entry.getValue() == null ? null : convertObjectTo(entry.getValue(), valueType);
newMap.put(key, value);
}
return Collections.unmodifiableMap(newMap);
}
/**
* Get the result of a Object path expression as a java Object.
* E.g. given the following Object document:
*
* { "store": {
* "book": [
* { "category": "reference",
* "author": "Nigel Rees",
* "title": "Sayings of the Century",
* "price": 8.95
* },
* { "category": "fiction",
* "author": "Evelyn Waugh",
* "title": "Sword of Honour",
* "price": 12.99
* },
* { "category": "fiction",
* "author": "Herman Melville",
* "title": "Moby Dick",
* "isbn": "0-553-21311-3",
* "price": 8.99
* },
* { "category": "fiction",
* "author": "J. R. R. Tolkien",
* "title": "The Lord of the Rings",
* "isbn": "0-395-19395-8",
* "price": 22.99
* }
* ],
* "bicycle": {
* "color": "red",
* "price": 19.95
* }
* }
* }
*
* And a Java object like this:
*
*
* public class Book {
* private String category;
* private String author;
* private String title;
* private String isbn;
* private float price;
*
* public String getCategory() {
* return category;
* }
*
* public void setCategory(String category) {
* this.category = category;
* }
*
* public String getAuthor() {
* return author;
* }
*
* public void setAuthor(String author) {
* this.author = author;
* }
*
* public String getTitle() {
* return title;
* }
*
* public void setTitle(String title) {
* this.title = title;
* }
*
* public String getIsbn() {
* return isbn;
* }
*
* public void setIsbn(String isbn) {
* this.isbn = isbn;
* }
*
* public float getPrice() {
* return price;
* }
*
* public void setPrice(float price) {
* this.price = price;
* }
* }
*
*
* Then
*
* Book book = from(Object).getObject("store.book[2]", Book.class);
*
*
* maps the second book to a Book instance.
*
* @param path The path to the object to map
* @param objectType The class type of the expected object
* @param The type of the expected object
* @return The object
*/
public T getObject(String path, Class objectType) {
Object object = getJsonObject(path);
if(object == null) {
return null;
} else if(object instanceof List || object instanceof Map) {
// TODO Avoid double parsing
object = new JsonBuilder(object).toString();
} else {
return convertObjectTo(object, objectType);
}
return ObjectMapping.deserialize(object, objectType, "application/json","", null);
}
/**
* Get the XML as a prettified string.
*
* @return The XML as a prettified String.
*/
public String prettify() {
return new Prettifier().prettify(JsonOutput.toJson(json), Parser.JSON);
}
/**
* Get and print the XML as a prettified string.
*
* @return The XML as a prettified String.
*/
public String prettyPrint() {
final String pretty = prettify();
System.out.println(pretty);
return pretty;
}
/**
* Instantiate a new JsonPath instance.
*
* @param text The text containing the Object document
*/
public static JsonPath given(String text) {
return new JsonPath(text);
}
/**
* Instantiate a new JsonPath instance.
*
* @param stream The stream containing the Object document
*/
public static JsonPath given(InputStream stream) {
return new JsonPath(stream);
}
/**
* Instantiate a new JsonPath instance.
*
* @param file The file containing the Object document
*/
public static JsonPath given(File file) {
return new JsonPath(file);
}
/**
* Instantiate a new JsonPath instance.
*
* @param reader The reader containing the Object document
*/
public static JsonPath given(Reader reader) {
return new JsonPath(reader);
}
/**
* Instantiate a new JsonPath instance.
*
* @param url The URL containing the Object document
*/
public static JsonPath given(URL url) {
return new JsonPath(url);
}
/**
* Instantiate a new JsonPath instance.
*
* @param stream The stream containing the Object document
*/
public static JsonPath with(InputStream stream) {
return new JsonPath(stream);
}
/**
* Instantiate a new JsonPath instance.
*
* @param text The text containing the Object document
*/
public static JsonPath with(String text) {
return new JsonPath(text);
}
/**
* Instantiate a new JsonPath instance.
*
* @param file The file containing the Object document
*/
public static JsonPath with(File file) {
return new JsonPath(file);
}
/**
* Instantiate a new JsonPath instance.
*
* @param reader The reader containing the Object document
*/
public static JsonPath with(Reader reader) {
return new JsonPath(reader);
}
/**
* Instantiate a new JsonPath instance.
*
* @param url The URI containing the Object document
*/
public static JsonPath with(URL url) {
return new JsonPath(url);
}
/**
* Instantiate a new JsonPath instance.
*
* @param stream The stream containing the Object document
*/
public static JsonPath from(InputStream stream) {
return new JsonPath(stream);
}
/**
* Instantiate a new JsonPath instance.
*
* @param text The text containing the Object document
*/
public static JsonPath from(String text) {
return new JsonPath(text);
}
/**
* Instantiate a new JsonPath instance.
*
* @param file The file containing the Object document
*/
public static JsonPath from(File file) {
return new JsonPath(file);
}
/**
* Instantiate a new JsonPath instance.
*
* @param reader The reader containing the Object document
*/
public static JsonPath from(Reader reader) {
return new JsonPath(reader);
}
/**
* Instantiate a new JsonPath instance.
*
* @param url The URI containing the Object document
*/
public static JsonPath from(URL url) {
return new JsonPath(url);
}
/**
* Set the root path of the document so that you don't need to write the entire path. E.g.
*
* final JsonPath jsonPath = new JsonPath(Object).setRoot("store.book");
* assertThat(jsonPath.getInt("size()"), equalTo(4));
* assertThat(jsonPath.getList("author", String.class), hasItem("J. R. R. Tolkien"));
*
*
* @param rootPath The root path to use.
*/
public JsonPath setRoot(String rootPath) {
notNull(rootPath, "Root path");
this.rootPath = rootPath;
return this;
}
private Object parseInputStream(final InputStream stream) {
return new ExceptionCatcher() {
protected Object method(JsonSlurper slurper) throws Exception {
return slurper.parse(toReader(stream));
}
}.invoke();
}
private Object parseReader(final Reader reader) {
return new ExceptionCatcher() {
protected Object method(JsonSlurper slurper) throws Exception {
return slurper.parse(reader);
}
}.invoke();
}
private Object parseFile(final File file) {
return new ExceptionCatcher() {
protected Object method(JsonSlurper slurper) throws Exception {
return slurper.parse(new FileReader(file));
}
}.invoke();
}
private Object parseURL(final URL url) {
return new ExceptionCatcher() {
protected Object method(JsonSlurper slurper) throws Exception {
return slurper.parse(toReader(url.openStream()));
}
}.invoke();
}
private BufferedReader toReader(InputStream in) {
return new BufferedReader(new InputStreamReader(in));
}
private abstract class ExceptionCatcher {
protected abstract Object method(JsonSlurper slurper) throws Exception;
public Object invoke() {
try {
return method(new JsonSlurper());
} catch(Exception e) {
throw new ParsePathException("Failed to parse the Object document", e);
}
}
}
public T getJsonObject(String path) {
final JSONAssertion jsonAssertion = createJsonAssertion(path);
return (T) jsonAssertion.getAsJsonObject(json);
}
private JSONAssertion createJsonAssertion(String path) {
notNull(path, "path");
final JSONAssertion jsonAssertion = new JSONAssertion();
final String root = rootPath.equals("") ? rootPath : rootPath.endsWith(".") ? rootPath : rootPath + ".";
jsonAssertion.setKey(root + path);
return jsonAssertion;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy