简体   繁体   中英

Convert json array to Seq of model class in Scala Play framework

I'm using Scala Play framework and Instagram API , and I want to extract a json array to my model class User :

case class User(val userId: String, val username: String, val profilePhoto: String, val name: String)

An json array example from the API is something like this:

{
  "pagination":  {},
  "meta":  {},
  "data":  [
     {
      "username": "carolinabentocb",
      "profile_picture": "https://igcdn-photos-f-a.akamaihd.net/hphotos-ak-xfa1/t51.2885-19/s150x150/11429783_1673078532912085_1496721162_a.jpg",
      "id": "363753337",
      "full_name": "Carolina Bento"
    },
     {
      "username": "pereira3044",
      "profile_picture": "https://igcdn-photos-e-a.akamaihd.net/hphotos-ak-xaf1/t51.2885-19/s150x150/11351764_1662987433917180_971708049_a.jpg",
      "id": "2141448590",
      "full_name": "Alex"
    }
]
}

In this link it is explained on how to map a json object to a model class, but how can I map the json array to a Seq/List/Array of Users?

The solution I've found is the following:

val users: Seq[User] = (json \ "data").as[JsArray].value.map(j => j.validate[User].get)

It may exist a more beautiful approach, but I will stick with this one until other answers.

The Json inception code is really great and it my preferred way to deserialize json. You will have to modify your User class to fit the instagram model API. Alternatively you could make a case class like InstagramApiUser or something to do the deserialization and copy to your own class later if you decide that is better for your flow. Here is the code and it works in a scala repl.

import play.api.libs.json.{Json, Format}


val js = Json.parse("""{
  "pagination":  {},
  "meta":  {},
  "data":  [
     {
      "username": "carolinabentocb",
      "profile_picture": "https://igcdn-photos-f-a.akamaihd.net/hphotos-ak-xfa1/t51.2885-19/s150x150/11429783_1673078532912085_1496721162_a.jpg",
      "id": "363753337",
      "full_name": "Carolina Bento"
    },
     {
      "username": "pereira3044",
      "profile_picture": "https://igcdn-photos-e-a.akamaihd.net/hphotos-ak-xaf1/t51.2885-19/s150x150/11351764_1662987433917180_971708049_a.jpg",
      "id": "2141448590",
      "full_name": "Alex"
    }
  ]
}""")

case class User(id: String, username: String, profile_picture: String, full_name: String)
object User {
  implicit val jsonFormat: Format[User] = Json.format[User]
}

val result = (js \ "data").as[Seq[User]]

There are three methods to deserialize Json in the Play Json library, and as is the least idiomatic one in my opinion as it throws an exception if it fails to parse. You could try using asOpt[A] which will produce an Option[A] or better validate[A] which will produce a JsResult[A] and then you can log an error with the reason(s) that parsing your Json failed.

If you don't like naming your case class members to match the API names you can write the Reads manually like

import play.api.libs.json.{Json, Reads, JsPath}
import play.api.libs.functional.syntax._

case class User(val userId: String, val username: String, val profilePhoto: String, val name: String)
object User {
  implicit val jsonReads: Reads[User] = (
    (JsPath \ "id").read[String] and
    (JsPath \ "username").read[String] and 
    (JsPath \ "profile_picture").read[String] and
    (JsPath \ "full_name").read[String]
  )(User.apply _)
}

And it works the same way otherwise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM