When using JSON combinators, one can create a recursive structure using lazyWrite
as stated in the documentation:
implicit lazy val userWrites: Writes[User] = (
(__ \ "name").write[String] and
(__ \ "friends").lazyWrite(Writes.seq[User](userWrites))
)(unlift(User.unapply))
Is it possible to do that while implementing writes, ie:
implicit lazy val userWrites: Writes[User] = new Writes[User]{
def writes(user: User) = Json.obj(
"name" -> user.name,
"friends" -> ??????
)
}
Yes. In fact, quite easy.
implicit lazy val userWrites: Writes[User] = new Writes[User] {
def writes(user: User) = Json.obj(
"name" -> user.name,
"friends" -> user.friends
)
}
val joe = User("Joe", Nil)
val bob = User("Bob", Nil)
val jane = User("Jane", Seq(bob, joe))
val james = User("James", Seq(bob, jane))
scala> Json.toJson(james)
res0: play.api.libs.json.JsValue = {"name":"James","friends":[{"name":"Bob","friends":[]},{"name":"Jane","friends":[{"name":"Bob","friends":[]},{"name":"Joe","friends":[]}]}]}
userWrites
doesn't really need to be lazy either. It works just fine like this because the combinators try to generate the writes by resolving child writes implicitly and working it's way down the tree, which is why it needs the lazyWrite
to stop it from descending infinitely in a recursive structure. When writing def writes
, we're explicitly stating what the writes are.
Neither, however, can save you from this scenario:
def jim: User = User("Joe", Seq(dwight))
def dwight: User = User("Bob", Seq(jim))