Gatling - Looping through JSON array

Neil picture Neil · Aug 13, 2014 · Viewed 24.3k times · Source

I have a block of code which needs to loop through a JSON array which is obtained from response of a REST service. (Full gist available here.)

.exec(http("Request_1")
  .post("/endPoint")
  .headers(headers_1)
  .body(StringBody("""REQUEST_BODY""")).asJSON
  .check(jsonPath("$.result").is("SUCCESS"))
  .check(jsonPath("$.data[*]").findAll.saveAs("pList")))
.exec(session => {
  println(session)
  session
})
.foreach("${pList}", "player"){
 exec(session => {
    val playerId = JsonPath.query("$.playerId", "${player}")
    session.set("playerId", playerId)
  })
 .exec(http("Request_1")
    .post("/endPoint")
    .headers(headers_1)
    .body(StringBody("""{"playerId":"${playerId}"}""")).asJSON
    .check(jsonPath("$.result").is("SUCCESS")))

}

The response format of the first request was

{
  "result": "SUCCESS",
  "data": [
    {
      "playerId": 2
    },
    {
      "playerId": 3
    },
    {
      "playerId": 4
    }
  ]
}

And playerId shows up in the session as

pList -> Vector({playerId=2, score=200}, {playerId=3, score=200}

I am seeing in the second request the body is

{"playerId":"Right(empty iterator)}

Expected : 3 requests with body as

 {"playerId":1}
 {"playerId":2}
 {"playerId":3}

I can loop over the resulting array successfully if I save just the playerIds:

.check(jsonPath("$.data[*].playerId").findAll.saveAs("pList")))

Answer

Michelle picture Michelle · Aug 19, 2014

I managed to get the requests you're looking for sent out (although still getting a 404, but that might be server-side or the request your gist is sending might be missing something). The trick was to give up on JsonPath entirely:

.exec(http("Request_10")
  .get("gatling1")
  .headers(headers_10)
  .check(jsonPath("$.result").is("SUCCESS"),
  jsonPath("$.data[*]").ofType[Map[String,Any]].findAll.saveAs("pList")))
.foreach("${pList}", "player") {
  exec(session => {
    val playerMap = session("player").as[Map[String,Any]]
    val playerId = playerMap("playerId")
    session.set("playerId", playerId)
  })

Here, the jsonPath check can automatically store your JSON object as a map, and then you can access the player ID by key. The value type doesn't have to be Any, you could use Int or Long if all your values are numbers. If you want more info on what went wrong with JsonPath, read on.


Your first problem is that JsonPath.query() doesn't just return the value you're looking for. From the JsonPath readme:

JsonPath.query("$.a", jsonSample) gives you Right(non-empty iterator). This will allow you to iterate over all possible solutions to the query.

Now, when it says Right(non-empty iterator), I assumed that meant the iterator was not empty. However, if you try this:

val playerId = JsonPath.query("$.playerId", session("player").as[String]).right.get
println(playerId)

...it prints "empty iterator". I'm not sure whether it's a problem with JsonPath, the jsonPath check, or usage somewhere in between, but there's not quite enough documentation for me to want to dig into it.