将 "recursive" 对象转换为 JSON (Play Framework 2.4 with Scala)

Converting "recursive" object to JSON (Play Framework 2.4 with Scala)

我已经达到了我的代码可以成功编译的地步,但我对我的解决方案有疑问,因此 post 提出这个问题。

我有一个节点 class 定义为:

case class Node(id: Long, label: String, parent_id: Option[Long])

我 quote/unquote 递归的原因是因为从技术上讲,我不在节点中存储节点。相反,每个节点都有一个指向其父节点的指针,我可以说:给我节点 id=X 的所有子节点。

为了可视化,这里有一个示例树。我想给出 root_node 的 ID,并获得树对 Json 字符串的转换:

root_node
|_ node_1
|  |_ node_11
|     |_ node_111
|_ node_2
|_ node_3

Json 看起来像:

{"title": "root_node", "children": [...]}

子数组包含 node_1、2 和 3 等...递归地

这是节点的写入转换器:

/** json converter of Node to JSON */
implicit val NodeWrites = new Writes[Node] {
  def writes(node: Node) = Json.obj(
    "title"  -> node.label,
    "children" -> Node.getChildrenOf(node.id)
  )
}

引用 Play 文档:

The Play JSON API provides implicit Writes for most basic types, such as Int, Double, String, and Boolean. It also supports Writes for collections of any type T that a Writes[T] exists.

我需要指出 Node.getChildrenOf(node.id) returns 来自数据库的节点列表。所以根据 Play 的文档,我应该能够将 List[Node] 转换为 Json。好像在Writes转换器内部做这个比较麻烦

这是 运行 此代码产生的错误:

type mismatch;
 found   : List[models.Node]
 required: play.api.libs.json.Json.JsValueWrapper
 Note: implicit value NodeWrites is not applicable here because it comes after the application point and it lacks an explicit result type

我将 "explicit result type" 添加到我的 Writes 转换器中,结果如下:

/** json converter of Node to JSON */
implicit val NodeWrites: Writes[Node] = new Writes[Node] {
  def writes(node: Node) = Json.obj(
    "title"  -> node.label,
    "children" -> Node.getChildrenOf(node.id)
  )
}

代码现在可以正常执行了,我可以在浏览器上看到这棵树。

尽管这在我看来是最干净的工作解决方案,但 IntelliJ 仍然抱怨该行:

"children" -> Node.getChildrenOf(node.id)

说:

Type mismatch: found(String, List[Node]), required (String, Json.JsValueWrapper)

难道 IntelliJ 的错误报告不是基于 Scala 编译器?

最后,JSON 转换器的整体方法是否糟糕?

感谢和抱歉 post。

问题出在"children" -> Node.getChildrenOf(node.id)Node.getChildrenOf(node.id)returns一个List[Node]。而 Json.obj 中的任何属性都需要 JsValueWrappers。在这种情况下 JsArray.

像这样的东西应该可以工作:

implicit val writes = new Writes[Node] {
  def writes(node: Node) = Json.obj(
    "title" -> node.label, 
    // Note that we have to pass this since the writes hasn't been defined just yet.
    "children" -> JsArray(Node.getChildrenOf(node).map(child => Json.toJson(child)(this)))
  )
}

这至少可以编译,不过我还没有用任何数据测试它。