简体   繁体   中英

Play Framework 2.1.1 Json Writes nested objects

I've been trying to get my head around this all afternoon to no avail, Play 2.1.1 overhauled how the Json reads and writes work.

Basically I have a wrapper object that looks like:

case class CombinedUser(user: SocialUser, userdetails: UserDetails)

as you can see it contains 2 classes that I want to serialize and deserialize to json.

But I don't understand how I can get and set the fields in the subclasses using the new design.

For example

implicit val combinedUser2Reads = (
  (__ \ "email").read[String] and
  (__ \ "providerid").read[String] and
  (__ \ "firstname").read[String] and
  (__ \ "lastname").read[String] and
  (__ \ "fullname").read[String] and
  (__ \ "avatarurl").read[String] and
  (__ \ "address1").read[String] and
  (__ \ "address2").read[String] and
  (__ \ "address3").read[String] and
  (__ \ "city").read[String] and
  (__ \ "country").read[String] and
  (__ \ "phone").read[String] and
  (__ \ "publickey").as[String]
)(CombinedUser2.apply _)

I want a json blob that has most of the subclasses fields, all strings.

Because it uses the apply stuff, I don't see how I can create the subclasses before the mapping.

Any help or guidance is much appreciated.

Thanks

Tom

You can do it this way:

case class SocialUser(firstName: String, lastName: String)
case class UserDetails(avatarUrl: String, phone: String)
case class CombinedUser(user: SocialUser, userDetails: UserDetails)  
implicit val combinedUserReads: Reads[CombinedUser] = (
  (__ \ "user").read((
    (__ \ "firstName").read[String] and
    (__ \ "lastName").read[String]
  )(SocialUser)) and
  (__ \ "userDetails").read((
    (__ \ "avatarUrl").read[String] and
    (__ \ "phone").read[String]
  )(UserDetails))
)(CombinedUser)

However its better to create separated Reads :

implicit val socialUserReads = (
  (__ \ "firstName").read[String] and
  (__ \ "lastName").read[String]
)(SocialUser)    
implicit val userDetailsReads = (
  (__ \ "avatarUrl").read[String] and
  (__ \ "phone").read[String]
)(UserDetails)
implicit val combinedUserReads: Reads[CombinedUser] = (
  (__ \ "user").read[SocialUser] and
  (__ \ "userDetails").read[UserDetails]
)(CombinedUser)

Edit: for simple case classes, its possible to do :

implicit val socialUserReads = Json.format[SocialUser]
implicit val userDetailsReads = Json.format[UserDetails]
implicit val combinedUserReads = Json.format[CombinedUser]

Here is quite comprehensive introduction to JSON Reads and more.

How about partial objects? If I don't want to fill in every field in the constructor, can I pass empties or do I overload the constructor or similar?

Use Option :

case class CombinedUser(user: SocialUser, userDetails: Option[UserDetails])
//reads
implicit val combinedUserReads: Reads[CombinedUser] = (
  (__ \ "user").read[SocialUser] and
  (__ \ "userDetails").readOpt[UserDetails]
)(CombinedUser)
//writes
implicit val combinedUserWrites: Writes[CombinedUser] = (
  //socialUserWrites and userDetailsWrites must be in scope
  (__ \ "user").write[SocialUser] and
  (__ \ "userDetails").write[Option[UserDetails]]
)(unlift(CombinedUser.unapply))

val json = Json.obj(
  "user" -> Json.obj(
    "firstName" -> "Homer",
    "lastName" -> "Simpson"
  )
)
Json.fromJson[CombinedUser](json)
//JsSuccess(CombinedUser(SocialUser(Homer,Simpson),None),)

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