Scala PlayJson 循环引用

Scala PlayJson Cyclic Reference

上下文

我有一个案例 class,它是层次结构中的一个项目,它是这样引用自身的:

case class Node(
  name:     String,
  children: Option[Seq[Node]] = None
)

为此我想要一个 PlayJson Format

通常,您可以这样做:

implicit lazy val formatter = Json.format[MyCaseClass]

但这行不通。

为什么?

PlayJson 使用 Scala 宏为案例 class 生成 Format,它将遍历所有字段,当到达字段 children 时,它将寻找一个Node 的现有格式化程序尚未构建,以编译错误结束:

No implicit format for Option[Seq[Node]] available.
[error]   implicit lazy val formatter = Json.format[Node]

问题

解决这个问题的最佳方法是什么?
这是 PlayJson 格式宏的已知问题吗?

这个东西可以在play-recursive types下面找到-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))

因为在那种情况下 ReadsWrites 是对称的,您可以将整个事物创建为单个 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))