Search code examples
scalaparsingargonaut

Parsing subdocuments in Argonaut


I'm trying to parse following Json with Argonaut scala library:

{"took":5,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hit
s":{"total":1285,"max_score":0.99627554,"hits":[{"_index":"logstash-2017.03.15",...

Parse.parseWith(posts, .field("took").flatMap(.string).getOrElse("42"), msg => msg) work fine to get the "took" field.

Now, I can't come up with a way to access nested values like hits->total. Another question, while we're at it would be how do I access list elements by index, like hits -> hits -> element 0 -> _index to get logstash-2017.03.15 in my case.


Solution

  • To get information out of the nested object you would have to drill down on the function being passed down (The functional way). By that I mean .map(.getField().map(.getField().map)) - You are going deeper and deeper within that field.

    You will also notice that the toInt types return a option thus we are using a flatMap

    import argonaut._, Argonaut._
    
    val posts: String = """{
                         |   "took":5,
                         |   "timed_out":false,
                         |   "_shards":{
                         |      "total":5,
                         |      "successful":5,
                         |      "failed":0
                         |   },
                         |   "hits":{
                         |      "total":1285,
                         |      "max_score":0.99627554,
                         |      "hits":[
                         |         {
                         |            "_index":"logstash-2017.03.15"
                         |         }
                         |      ]
                         |   }
                         |}""".stripMargin
    
    
    val took = Parse.parseWith(posts, _.field("took").flatMap(_.number.flatMap(_.toInt)).getOrElse(0), msg => 0)
    
    val hitsTotal = Parse.parseWith(posts, _.field("hits").flatMap(
                                              _.field("total").flatMap(_.number.flatMap(_.toInt))
                                           ).getOrElse(0), msg => 0)
    
    val hitsList = Parse.parseWith[List[Json]](posts, _.field("hits").flatMap(_.field("hits").flatMap(_.array)).getOrElse(List()), msg => List())
    

    Result:

    took: Int = 5
    hitsTotal: Int = 1285
    hitsList: List[argonaut.Json] = List({"_index":"logstash-2017.03.15"})
    

    You will notice that hitsList is just a normal List containing more JSON that you can use .field() on