Context
I have a case class which is an item in a hierarchy, which refers to itself like so:
case class Node(
name: String,
children: Option[Seq[Node]] = None
)
I would like a PlayJson Format
for this.
Usually, you can just do:
implicit lazy val formatter = Json.format[MyCaseClass]
But this doesn't work.
Why?
PlayJson uses a Scala macro to produce a Format
for the case class, it will go through all fields, when it gets to the field children
it will look for an existing formatter for Node
which it hasn't constructed yet, ending with a compilation error:
No implicit format for Option[Seq[Node]] available.
[error] implicit lazy val formatter = Json.format[Node]
Questions
What's the best way to approach this?
Is this a known issue with PlayJson format macro?
This is something that can be found under recursive types
in play-json docs:
import play.api.libs.functional.syntax._
import play.api.libs.json.{Reads, Writes, _}
case class Node(name: String, children: Option[Seq[Node]] = None)
implicit lazy val nodeReads: Reads[Node] = (
(__ \ "name").read[String] and
(__ \ "children").lazyReadNullable(Reads.seq[Node](nodeReads))
)(Node)
implicit lazy val nodeWrites: Writes[Node] = (
(__ \ "name").write[String] and
(__ \ "children").lazyWriteNullable(Writes.seq[Node](nodeWrites))
)(unlift(Node.unapply))
Since in that case Reads
and Writes
are symmetrical, you can create the whole thing as a single Format
:
implicit lazy val nodeFormat: Format[Node] = (
(__ \ "name").format[String] and
(__ \ "children").lazyFormatNullable(Reads.seq[Node](nodeFormat), Writes.seq[Node](nodeFormat))
)(Node.apply, unlift(Node.unapply))
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