Using JsonPath and XmlPath in REST Assured

While preparing my REST Assured workshop for the Romanian Testing Conference next month, I ran into a subject I feel I didn’t cover enough in the previous times I hosted the workshop: how to effectively use JsonPath and XmlPath to extract specific elements and element groups in RESTful API responses before verifying them. Here are a couple of tricks I learned since and worked into the exercises that’ll be part of the workshop from now on.

The examples in this post are all based on the following XML response:

<?xml version="1.0" encoding="UTF-8" ?>
<cars>
	<car make="Alfa Romeo" model="Giulia">
		<country>Italy</country>
		<year>2016</year>
	</car>
	<car make="Aston Martin" model="DB11">
		<country>UK</country>
		<year>1949</year>
	</car>
	<car make="Toyota" model="Auris">
		<country>Japan</country>
		<year>2012</year>
	</car>
</cars>

The syntax for JsonPath is very, very similar, except for the obvious lack of support for attributes in JsonPath (JSON does not have attributes).

Extracting a single element based on its index
Let’s get started with an easy example. Say I want to check that the first car in the list is made in Italy. To do this, we can simply traverse the XML tree until we get to the right element, using the index [0] to select the first car in the list:

@Test
public void checkCountryForFirstCar() {
						
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car[0].country", equalTo("Italy"));
}

Similarly, we can check that the last car came on the market in 2012, using the [-1] index (this points us to the last item in a list):

@Test
public void checkYearForLastCar() {
						
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car[-1].year", equalTo("2012"));
}

Extracting an attribute value
Just as easily, you can extract and check the value of an attribute in an XML document. If we want to check that the model of the second car in the list is ‘DB11’, we can do so using the ‘@’ notation:

@Test
public void checkModelForSecondCar() {
						
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car[1].@model", equalTo("DB11"));
}

Counting the number of occurrences of a specific value
Now for something a little more complex: let’s assume we want to check that there’s only one car in the list that is made in Japan. To do this, we’ll need to apply a findAll filter to the country element, and subsequently count the number of items in the list using size():

@Test
public void checkThereIsOneJapaneseCar() {
		
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car.findAll{it.country=='Japan'}.size()", equalTo(1));
}

Likewise, we can also check that there are two cars that are made either in Italy or in the UK, using the in operator:

@Test
public void checkThereAreTwoCarsThatAreMadeEitherInItalyOrInTheUK() {
		
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car.findAll{it.country in ['Italy','UK']}.size()", equalTo(2));
}

Performing a search for a specific string of characters
Finally, instead of looking for exact attribute or element value matches, we can also filter on substrings. This is done using the grep() method (very similar to the Unix command). If we want to check the number of cars in the list whose make starts with an ‘A’, we can do so like this:

@Test
public void checkThereAreTwoCarsWhoseMakeStartsWithAnA() {
		
	given().
	when().
		get("http://localhost:9876/xml/cars").
	then().
		assertThat().
		body("cars.car.@make.grep(~/A.*/).size()", equalTo(2));
}

If you know of more examples, or if I missed another example of how to use JsonPath / XmlPath, do let me know!

46 thoughts on “Using JsonPath and XmlPath in REST Assured

  1. Pingback: Java Web Weekly, Issue 174 | Baeldung

  2. Pingback: Testing Bits – 4/23/17 – 4/29/17 | Testing Curator Blog

  3. Pingback: Java Testing Weekly 18 / 2017

  4. Hi Bas,

    I’m trying to use REST Assured to verify an endpoint that returns XML (namespace) by default, but ran into the following error:-

    “java.lang.IllegalStateException: Expected response body to be verified as JSON, HTML or XML but content-type ‘application/octet-stream’ is not supported out of the box.
    Try registering a custom parser using: RestAssured.registerParser(“application/octet-stream”, );”

    How do have any examples on how I can solve this problem?

    Thanks!

    • Does the XML response actually contain a document or is the content type not set correctly? REST Assured parses response bodies based on their content type.

      If the response can be interpreted as XML, you can try registering the XML parser for your content type like this:

      RestAssured.registerParser(“application/octet-stream”, Parser.XML);

      More information can be found here.

      Hope that helps!

  5. What is the syntax in RestAssured to extract a block of XML (not values or attriubutes)?

    For example, what if I wanted to extract the following, and store in, perhaps, a String to build a further xml request? :

    Italy
    2016

    UK
    1949

    Japan
    2012

  6. Hi Bas,

    Thanks for the article . I have a question
    is this better approach to test or -mapping the entire json to an object (of a Class ) and then validating the same.

    Can this approach(json path) handle all type of scenarios we are trying to validate

    • Hey,

      as any consultant would say: ‘it depends’. If all you’re interested in is a single element, then creating a POJO (if it doesn’t already exist) and deserializing your JSON or XML response body to an instance of that POJO is overkill, and you can use JsonPath / XmlPath instead.

      If you want to do multiple asserts or even a full body assert, then I’d say go for the (de-)serialization route. Less JsonPath / XmlPath entries to maintain in that way.

      It’s all what you’re comfortable with with regards to creation and maintenance, too.

  7. Helpful article, thanks.
    I suggest a little addition, since a typical use case for me is something like “assert that object with id=x has attribute=y”.

    In your example this could be:”assert that model Guilia is made in italy”

    which I would implement as follows:

    assertThat().body(“cars.find{model==’Guilia’}.country”,equalTo(“Italy”));

  8. Do you have any insights on using the grep method as per your last example, when trying to find a specific word within an XML attribute? For example, the following XML snippet:

    I am trying to traverse XML containing many OptionalExtras but I only want the ‘Code’ attribute where ‘Description’ contains ‘Child’

    Using the following throws no exceptions but fails to find the element:

    List allChildMeals;
    allChildMeals = response.xmlPath().getList(“OptionalExtra.findAll{it.@Type==’Meal’ && it.@Description.grep(/Child/)}*.@Code”);

  9. Sorry, I should have included AND specified the source data was XML. A snippet is as follows:

    I can’t seem to find anything in the GPath docs to suggest a way.

    • Now, I haven’t tested this, and it looks a little hacky, but what happens if you try:

      body(“path.to.your.@attr.grep(~/.*/).size()”, equalTo(0));

      Basically, you traverse the tree to where your attribute shouldn’t appear, grep for any string value (should match all possible values) and check that the size is 0.

      It doesn’t look elegant, so that means there is probably a better solution, but it’s worth a try I think. In the meantime, I’ll keep thinking.

  10. Can you explain What does ** means in the below code snippet:

    XML

    Chocolate
    10

    Coffee
    20

    Get the chocolate price:
    int priceOfChocolate = with(XML).getInt(“**.find { it.name == ‘Chocolate’ }.price”

    Or can you briefly explain the meaning of each verb in it?

  11. I need to validate a xml request and xml reponse in rest interface. I tried the url with rest assured through eclipse. since am not able to pass the raw xml body via the code, the response code is 415 and not getting status code as 200 and no response body.

    Please help to parse the xml request and send the post rest request and get xml response and valdiate it with the account number as parameter

    • Hey Vinothini,

      what does your code look like? What’s the content type you specified for the request? You could try and insert log().all() after the given to see what exactly is being sent in the API request. Also, personally I find working with Java objects and then serializing them to XML much easier than working with XML directly. In this post I explain how to do this for JSON, but it works in much the same manner for XML.

  12. Hello, Is there a way to perform a grep on a JsonPath? Is my format below incorrect?
    No matter the input the test is passing (tried putting in random numbers and such into the grep to mimic a failure).
    body(“accountsDetails.@accountType.grep(~/9.*/).status”, notNullValue()).

    Trying to traverse this where I only search for a certain accountType and ensure that the status is not null:

    “accountsDetails”: [
    {
    “accountId”: “67890”,
    “accountNumber”: “12345”,
    “accountType”: “abcde123”,
    “status”: “ACTIVE”,
    }

      • Oh wait, this is a little more complicated. Could you try this: body(“accountsDetails.findAll{it.accountType.grep(~/9.*/).status”, notNullValue())

        Typing this in my phone so I’ve no way to test it. If you can’t get it to work I’ll happily try and find out after the weekend. Let me know!

        • Thank you for such a fast response and the help! The test is still passing though, which tells me something isn’t quite right here. This is what I put in.

          body(“accountsDetails.findAll {it.accountType.grep(~/9.*/)}.status”, notNullValue()).

  13. @Test
    public void test_NumberOfCircuitsFor2017Season_ShouldBe20() {

    given().
    when().
    get(“http://ergast.com/api/f1/2017/circuits.json”).
    then().
    assertThat().
    body(“MRData.CircuitTable.Circuits.circuitId”,hasSize(20));
    }

    Getting error

    java.lang.AssertionError: 1 expectation failed.
    XML path MRData.CircuitTable.Circuits.circuitId doesn’t match.
    Expected: a collection with size

    • That’s strange. It tries to parse the JSON response as XML, so it uses XmlPath, which doesn’t work with JSON. Could you upload your project to Github? I’d like to have a look.

  14. I have a JSON Body returning me around 1000 results for example :
    {
    “message”: “Search retrieved successfully”,
    “searchResults”: [
    {
    “searchFields”: {
    “Country”: “CA”,
    “SourceId”: “2”
    }
    },
    {
    “searchFields”: {
    “Country”: “CA”,
    “SourceId”: “1”
    }
    },
    {
    “searchFields”: {
    “Country”: “US”,
    “SourceId”: “1”
    }
    }
    ]
    }

    I want to find out if a my json response has records Only containing SourceId as 1 and Country as US. In this case only 1 would match. How can i do this efficiently if I have lot of records

      • I am trying to this response.then().assertThat().body(“searchResults.searchFields.findAll{it.SourceId}”,hasItems(2220)); But need better solution , Can you please guide on how to solve it.

        • What do you want to do? Now all you’re doing is counting the number of SourceId items and you don’t need findAll for that.

Leave a Reply to Vinothini Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.