0

I have 3 case classes:

scala> case class Friend(id: Long, hobbies: List[Long])
defined class Friend

scala> case class Kid(friends: List[Friend])
defined class Kid

scala> case class Parent(kids: List[Kid])
defined class Parent

They can be shown through a hierarchy (Parent has Kid(s) has Friend(s) has (id and hobbies).

Parent   
   ---> Kid
      ---> Friend
           ---> id: Long
           ---> hobbies: List[Long]

I created the FriendsReads (even though, as senia pointed out here, I could've just used

scala> implicit val FriendReads: Reads[Friend] = Json.format[Friend]
FriendReads: play.api.libs.json.Reads[Friend] = play.api.libs.json.OFormat$$anon$1@236b2692

scala> implicit val FriendReads: Reads[Friend] = (
     |   (JsPath \ "id").read[Long] and
     |    (JsPath \ "hobbies").read[List[Long]]
     | )(Friend.apply _)
FriendReads: play.api.libs.json.Reads[Friend] = play.api.libs.json.Reads$$anon$8@4bb7bb4

Then, when I tried to create the KidReads, I ran into a compile-time problem.

scala> implicit val KidReads: Reads[Kid] = (
     |   (JsPath \ "friends").read[List[Friend]]
     | )(Kid.apply _)
<console>:40: error: overloaded method value read with alternatives:
  (t: List[Friend])play.api.libs.json.Reads[List[Friend]] <and>
  (implicit r: play.api.libs.json.Reads[List[Friend]])play.api.libs.json.Reads[List[Friend]]
 cannot be applied to (List[Friend] => Kid)
         (JsPath \ "friends").read[List[Friend]]
                              ^

How can I resolve this error?

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384

1 Answers1

1

Expression (JsPath \ "friends").read[List[Friend]] creates Reads[List[Friend]], you could convert it to Reads[Kid] using map method like this:

implicit val KidReads: Reads[Kid] =
  (JsPath \ "friends").read[List[Friend]] map Kid.apply

Function Kid.apply will be applied to result of Reads[List[Friend]]#reads like this:

val friendListReads: Reads[List[Friend]] = (JsPath \ "friends").read[List[Friend]]

implicit val kidReads: Reads[Kid] = new Reads[Kid] {
  def reads(json: JsValue): JsResult[Kid] = friendListReads.reads(json) match {
    case JsSuccess(fList, path) = JsSuccess(Kid(fList), path)
    case e: JsError => e
  }
}
senia
  • 37,745
  • 4
  • 88
  • 129
  • When mapping over Reads[List[Friend]], map's function f has signature: List[Friend] -> Friend? – Kevin Meredith Feb 07 '14 at 02:12
  • I'm having trouble reasoning about this map function. Why does applying Kid.map to each Friend work? – Kevin Meredith Feb 07 '14 at 10:48
  • thank you. If `Friend`'s second field changed to `case class Friend(id: Long, hobbies: List[SomeObject])`, would I then need to define a `Reads[SomeObject]`? I'm trying to get a grip on when it's necessary to define a `Reads` or `Writes`. – Kevin Meredith Feb 11 '14 at 15:27
  • @KevinMeredith: Yes, you have to define `Reads` for all classes you want to read from json. – senia Feb 11 '14 at 16:02