Fetching nested JSON values from session attributes

I am trying to parse the following response and do some calculation on id and version .

`

{
  "result": "SUCCESS",
  "data": [
    {
      "playerId": {
          "id":2,
          "version":1
      },
      "score":200
    }
  ]
}

`

Code is

`
package computerdatabase

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
import io.gatling.jsonpath._

class Test1 extends Simulation {

//Global Users
val userMap = scala.collection.concurrent.TrieMapString, String
//Randomiser
val rnd = new scala.util.Random
val httpConf = http
.baseURL(“http://demo1263864.mockable.io/”) // Here is the root for all relative URLs
.acceptHeader(“application/json”) // Here are the common headers
.doNotTrackHeader(“1”)
.acceptLanguageHeader(“en-US,en;q=0.5”)
.acceptEncodingHeader(“gzip, deflate”)
.userAgentHeader(“Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0”)

val headers_10 = Map(“Content-Type” → “application/json”) // Note the headers specific to a given request

val scn = scenario(“Simple request”)
.exec(
http(“If request “)
.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”).asInstanceOf[Map[String, Any]]
val id = playerId(“id”)
println(id)
session.set(“playerId”, playerId)
})
}

setUp(scn.inject(atOnceUsers(1)).protocols(httpConf))
}Enter code here…
`

The line


val playerId = playerMap(“playerId”).asInstanceOf[Map[String, Any]]

Errors out with


20:36:15.860 [ERROR] i.g.c.a.b.SessionHookBuilder$$anonfun$build$1$$anon$1 - Action anonfunbuild1anon1 crashed on session Some(Session(Simple request,4173992086124673397-0,Map(8d6aa6d4-3c7e-4e8f-b475-db8bfe5094e1 → 0, pList → Vector(Map(playerId → {id=2, version=1}, score → 200)), timestamp.8d6aa6d4-3c7e-4e8f-b475-db8bfe5094e1 → 1408547175843, player → Map(playerId → {id=2, version=1}, score → 200), gatling.http.cookies → CookieJar(Map())),1408547175294,71,OK,List(ExitOnCompleteLoopBlock(8d6aa6d4-3c7e-4e8f-b475-db8bfe5094e1)))), forwarding to the next one
java.lang.ClassCastException: org.boon.core.value.LazyValueMap cannot be cast to scala.collection.immutable.Map
at computerdatabase.Test1$$anonfun$1.apply(Test1.scala:34) ~[na:na]
at computerdatabase.Test1$$anonfun$1.apply(Test1.scala:32) ~[na:na]
at io.gatling.core.action.SessionHook.executeOrFail(SessionHook.scala:35) ~[gatling-core-2.0.0-RC2.jar:2.0.0-RC2]
at io.gatling.core.action.Failable$class.execute(Actions.scala:71) ~[gatling-core-2.0.0-RC2.jar:2.0.0-RC2]
at io.gatling.core.action.SessionHook.execute(SessionHook.scala:28) ~[gatling-core-2.0.0-RC2.jar:2.0.0-RC2]
at io.gatling.core.action.Action$$anonfun$receive$1.applyOrElse(Actions.scala:29) ~[gatling-core-2.0.0-RC2.jar:2.0.0-RC2]
at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:166) ~[scala-library.jar:na]
at akka.actor.Actor$class.aroundReceive(Actor.scala:465) ~[akka-actor_2.10-2.3.4.jar:na]
at io.gatling.core.akka.BaseActor.aroundReceive(BaseActor.scala:23) ~[gatling-core-2.0.0-RC2.jar:2.0.0-RC2]
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516) ~[akka-actor_2.10-2.3.4.jar:na]
at akka.actor.ActorCell.invoke(ActorCell.scala:487) ~[akka-actor_2.10-2.3.4.jar:na]
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:238) ~[akka-actor_2.10-2.3.4.jar:na]
at akka.dispatch.Mailbox.run(Mailbox.scala:220) ~[akka-actor_2.10-2.3.4.jar:na]
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393) [akka-actor_2.10-2.3.4.jar:na]
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library.jar:na]
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library.jar:na]
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library.jar:na]
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library.jar:na]

How do I handle nested maps ?

Ah, this actually looks like a bug…
Will investigate.

In the mean time, you can still get each value separately :

`
.check(
jsonPath("$.result").is(“SUCCESS”),
jsonPath("$.data.playerId.id").ofType[Seq[Int]].findAll.saveAs(“playerIds”)
jsonPath("$.data.playerId.version").ofType[Seq[Int]].findAll.saveAs(“playerVersions”)
)

`

This will save one list of ids and one list of versions in the session, which you can then put back together should you need to :

`
case class Player(id: Int, version: Int)

.exec(session => {
val playerIds = session(“playerIds”).as[Seq[Int]]
val playerVersions = session(“playerVersions”).as[Seq[Int]]
val players = (playerIds, playerVersions).zipped.map(Player.apply))
session.set(“players”, players)
})
`

This last piece of code :

  • fetches the two lists from the session

  • “zips” the lists, e.g. (List(1,3), List(2, 4) ==> List((1,2), (3,4))

  • Builds a list of Player from the previous lists

Hope this helps !

Cheers,

Pierre

Thanks Pierre,
I did think about this individual list workaround . The problem is that my actual JSON format is deeply nested and complex in nature ,so wanted a straight forward way to deal with a block of JSON at a time. Basically the requirement is similar to obtaining those playerIDs whose version is 1. There is an old post which uses some Json library to do so but I wanted to have a loop and filter.

Still I will try this approach.
Let me know in case you decide to file a bug so that I can follow.

I settled for the following which is similar to Pierre’s code

`
.check(
jsonPath("$.result").is(“SUCCESS”),

jsonPath("$.data.playerId.id").findAll.saveAs(“playerIds”)
jsonPath("$.data.playerId.version").findAll.saveAs(“playerVersions”)
)

`

Note: casting to as[Seq[Int]] game me error
`

.exec(session => {

val playerIds = session(“playerIds”).as[Vector[String]]
val playerVersions = session(“playerVersions”).as[Vector[String]]
playerIds.indices.foreach { i =>
if (playerVersions(i) == 1) {
filteredPlayerIds += playerIds(i)
}
}
})
`

Anything other than as[Vector[String]] gave me error

Hi Neil,

You need to specify that id and version are to be treated as Ints, or they’re saved as Strings.

This works (unfortunately there was a slight error in my previous example) :

jsonPath("$.data.playerId.id").ofType[Int].findAll.saveAs("playerIds") jsonPath("$.data.playerId.version").ofType[Int].findAll.saveAs("playerVersions")

Then you can get it back as a Vector[Int].

Cheers,

Pierre

Hi,

Though an old thread, I thought I’d post a suggestion as I’ve come across a similar situation.

I found that the Gatling JsonPath implementation (import io.gatling.jsonpath.JsonPath) with its filter conditions could work here (at least for simple filter logic):

`
jsonPath("$.data[?(@.playerId.version == 1)]").ofType[Any].findAll.saveAs(“filteredPlayers”)

`
Then you can do what you like with the saved player objects later on.

Or, if you’re only interested in the ids, to get your Vector[Int]:

`
jsonPath("$.data[?(@.playerId.version == 1)]").ofType[Any].findAll
.transform(_.map(JsonPath.query("$.playerId.id", _).right.get.next.asInstanceOf[Int]))
.saveAs(“filteredPlayerIds”)

`

However, if your filter expression is too complex for JsonPath, you could do:

jsonPath("$.data[*]").ofType[Any].findAll .transform(_.filter(JsonPath.query("$.playerId.version", _).right.get.next.asInstanceOf[Int] == 1)) .saveAs("players")

Anyhow, the transform step eliminates the need for your forEach loop.

HTH,
Till

Yep, you can do that.
But you’d better compile your paths once and for all if they’re static, instead of over and over again.

hi,
{“id”:30,“result”:{“moreRows”:0,“landmarkList”:[{“id”:“40542263”,“lastUpdate”:“2016-07-15 16:27:45”},{“id”:“40542309”,“lastUpdate”:“2016-07-15 16:27:45”},{“id”:“47891637”,“lastUpdate”:“2016-07-15 16:27:45”},{“id”:“47891639”,“lastUpdate”:“2016-07-15 16:27:45”},{“id”:“40542383”,“lastUpdate”:“2016-07-15 16:27:45”}]}}

how can i get id’s from the above json

Thanks in Advance