13

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")))
Michelle
  • 2,830
  • 26
  • 33
Neil
  • 5,919
  • 15
  • 58
  • 85

1 Answers1

19

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.

Michelle
  • 2,830
  • 26
  • 33
  • WHich Map import did you use ? For me it shows error Cannot find JsonFilter type class for type scala.collection.Map[String,Any] 00:28:05.986 [ERROR] i.g.a.ZincCompiler$ - ,jsonPath("$.data[*]").ofType[Map[String,Any]].findAll.saveAs("pList"))) – Neil Aug 19 '14 at 19:00
  • 3
    @NeilGhosh I didn't import anything. What version of Gatling are you using? `ofType` and/or `ofType[Map]` might be new in RC1 or RC2. – Michelle Aug 19 '14 at 19:02
  • gatling-charts-highcharts-2.0.0-RC2 – Neil Aug 19 '14 at 19:03
  • Sorry I had the extra import scala.collection.Map , removed that to get rid of this error – Neil Aug 19 '14 at 19:13
  • With with the dataType Any I have a problem in comparison. Basically the player ID is a number so if (playerId < ${"playerIdFromSession"} giving me compilation error value < is not a member of Any) – Neil Aug 19 '14 at 19:23
  • Perfect ! This is what happens when you try Gatling before learning Scala :) The following worked if (playerId.asInstanceOf[Long] < session("playerIdFromSession").as[Long]) { – Neil Aug 19 '14 at 19:32
  • Just another quick question, in your answer playerId is a String so we directly assigned it to a variable. In my real test case playerId is a map again , when I typcast using playerId..asInstanceOf[Map[String, Any]] it gives me error java.lang.ClassCastException: org.boon.core.value.LazyValueMap cannot be cast to scala.collection.immutable.Map. I was wondering how to handle nested JSON after getting type "Any" from session – Neil Aug 20 '14 at 13:55
  • @NeilGhosh That I'm not sure of, and may be a limitation of the implementation of the `jsonPath` check. I think your best bet would be to post a new question on the Google group specifically regarding `jsonPath().ofType[]` and whether it can handle nested maps/how to do so. – Michelle Aug 20 '14 at 14:01
  • Thanks Michelle I'll do that in a while with a proper test case , was just wondering if you had some suggestion over the top of you head :) – Neil Aug 20 '14 at 14:32