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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With