37

I have a POJO Artwork. I'm retrieving a List of those objects from a RESTful webservice in the HTTP response body in JSON format. I'm trying to write a Rest Assured-based test that would analyze the returned list. The code looks like this:

Response response = get("/artwork");
List returnedArtworks = response.getBody().as(List.class)

The problem is, I can't get Rest Assured to parse the returned JSON as List<Artwork>. Instead, I get a List<LinkedHashMap>. The map has a proper structure, i.e. could be mapped by Jackson to Artwork object, but I'd like to avoid mapping it manually.

JSON mappings in my model are OK, because when I map single object like this:

Artwork returnedArtwork = response.getBody().as(Artwork.class);

it works fine.

Is it possible to get returnedArtworks as List<Artwork>?

Wojtek
  • 2,514
  • 5
  • 26
  • 31

6 Answers6

44

You can do this:

List<Artwork> returnedArtworks = Arrays.asList(response.getBody().as(Artwork[].class));

The trick is to deserialize JSON to an array of objects (because there is no difference between the JSON string of an array or a list), then convert the array to a list.

volatilevar
  • 1,626
  • 14
  • 16
  • You should add an explanation to the solution for others who find this. – Ian Stapleton Cordasco Aug 03 '14 at 13:49
  • Thanks, I added a brief explanation (and corrected an error). – volatilevar Aug 03 '14 at 13:57
  • Can't check it right now, but assuming all the upvotes coming from users who found this solution useful, I'm marking this as accepted answer – Wojtek Apr 07 '17 at 15:17
  • I upvoted this answer, since it's concise. But it's not working for me. I've spent two hours trying to fix it, although I get this error: `com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of ................. ` – Jose4Linux Jun 03 '20 at 23:18
13

this solution works for version 3.0.2 (io.restassured):

  JsonPath jsonPath = RestAssured.given()
     .when()
     .get("/order")
     .then()
     .assertThat()
     .statusCode(Response.Status.OK.getStatusCode())
     .assertThat()
     .extract().body().jsonPath();

  List<Order> orders = jsonPath.getList("", Order.class);

This will extract the objects for a structure like this:

public class Order {

private String id;

public String getId(){
return id; }

public void setId(String id){
this.id = id;
}


}

with the given json:

[ 
{ "id" : "5" }, 
{ "id" : "6" }
]
Seren
  • 131
  • 1
  • 5
  • I get: `java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class java.util.List (java.util.LinkedHashMap and java.util.List are in module java.base of loader 'bootstrap')` – Perkone Jan 17 '22 at 15:31
5

By using Google's Gson library you can easily parse it to List<Artwork>. Try below code

Gson gson = new Gson();
List<Artwork> returnedArtworks = gson.fromJson(jsonStr, new TypeToken<List<Artwork>>(){}.getType());

//* where jsonStr is the response string(Json) receiving from your Restful webservice
Purushotham
  • 3,770
  • 29
  • 41
  • Thank you for the answer. Though, it's exactly what I wanted to avoid: "The map has a proper structure, i.e. could be mapped by Jackson to Artwork object, but I'd like to avoid mapping it manually." I was thinking if there's any chance to do it more or less like this: `List returnedArtworks = response.getBody().asListOf(Artwork.class)` – Wojtek Feb 13 '14 at 09:02
  • If your Artwork class is exactly matching with the map you are receiving from response, you can use Gson instead of Jackson. You can reuse the same Artwork class. – Purushotham Feb 13 '14 at 09:30
  • It doesn't really matter that much whether I use Gson or Jackson. In either case I'd have to write an additional line of code for parsing the map received in response. I hoped that Rest Assured had it implemented somehow, as it is quite often that we receive a `List` of objects in response. – Wojtek Feb 13 '14 at 09:46
  • `TypeToken#getType()` can be used directly as parameter in the rest-assured `.as()`. See my answer: https://stackoverflow.com/a/57373937/91497 – Jmini Aug 06 '19 at 10:27
  • that's working solution!! – Maksat Rahmanov Jul 21 '23 at 16:30
2

Rest-assured provide an as(java.lang.reflect.Type) next to the version expecting a Class used in the question.

java.lang.reflect.Type type; //TODO defines the type.
Response response = get("/artwork");
List<Artwork> returnedArtworks = response.getBody().as(type)

In my opinion the way the type variable depends from the serialization lib that is used.


If using Gson, as pointed out by Purushotham's answer, TypeToken can be used. I prefer using it directly in rest-assured:

Type type = new TypeToken<List<Artwork>>(){}.getType();
Response response = get("/artwork");
List<Artwork> returnedArtworks = response.getBody().as(type)

When using Jackson, the solution is to use the TypeFactory (javadoc, source) to tell to which type it should be de-serialized:

Type type = TypeFactory.defaultInstance().constructCollectionLikeType(ArrayList.class, Artwork.class);
Response response = get("/artwork");
List<Artwork> returnedArtworks = response.getBody().as(type)
Jmini
  • 9,189
  • 2
  • 55
  • 77
0

With REST assured 3.0.2 you can simply check if content exists in the array

when().get("/artwork").then().body("artworks", hasItem("some art");
//or check multiple values in array
when().get("/artwork").then().body("artworks", hasItems("some art", "other art");

This way you will avoid complexity in your code by converting JSON to list more examples how to check response content can be found link

Vytautas
  • 77
  • 4
0

Currently we have a better solution using Typeref which will help to extract generic objects

List<Artwork> returnedArtworks = response.getBody().as(new TypeRef<List<Artwork>>() {});

or

response.as(new TypeRef<List<Artwork>>() {});