Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to traverse JSON object fields using JsPath?

Take into account the following JSON provided by a vendor API:

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._

val json = Json.parse(
  """
    |{
    |  "returns": {
    |    "markets" : {
    |      "ABC" : {
    |        "label": "ABC",
    |        "id":1
    |      },
    |      "DEF" : {
    |        "label": "DEF",
    |        "id":2
    |      }
    |    }
    |  }
    |}
  """.stripMargin)

How to extract a sequence of pairs related to "label" and "id" fields. From this piece of JSON the result I'm expecting is:

Seq((1,"ABC"),(2,"DEF"))

I'm failing with constructing a correct JsPath extractor because it expects a single match e.g.

val jsonTransformer = (__ \ 'returns \ 'markets).json.pick
json.transform(jsonTransformer)
like image 827
Bartosz Jankiewicz Avatar asked Sep 30 '22 12:09

Bartosz Jankiewicz


1 Answers

Here's how I'd build this parser out of nice composable pieces. First for a general purpose object-to-array transformer that throws away keys:

val objToArray: Reads[JsArray] =
  JsPath.json.pickBranch.map(obj => JsArray(obj.fields.map(_._2)))

Now for a Reads that can process market objects:

val marketReads: Reads[(Int, String)] =
  ((__ \ 'id).read[Int] and (__ \ 'label).read[String]).tupled

And now we tie it all together:

val pairsReads: Reads[List[(Int, String)]] = 
  (__ \ 'returns \ 'markets).read(objToArray) andThen list(marketReads)

And finally:

scala> json.validate(pairsReads).foreach(println)
List((1,ABC), (2,DEF))

Which is what you want. Note that I've specified the types above for clarity, but that's not necessary here—I'd probably leave them out in real code because the pieces are pretty small and straightforward.

like image 127
Travis Brown Avatar answered Oct 18 '22 20:10

Travis Brown