【问题标题】:Converting "recursive" object to JSON (Play Framework 2.4 with Scala)将“递归”对象转换为 JSON(Play Framework 2.4 with Scala)
【发布时间】:2016-08-09 17:48:10
【问题描述】:

我的代码已经成功编译,但我对我的解决方案有疑问,因此我发布了这个问题。

我有一个 Node 类定义为:

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

我引用/取消引用递归的原因是因为从技术上讲,我不会将节点存储在节点中。相反,每个节点都有一个指向其父节点的指针,我可以说:给我节点 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 等的子数组递归地...

这是 Node 的写入转换器:

/** 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 文档:

Play JSON API 为大多数基本类型提供隐式写入,例如 作为 Int、Double、String 和 Boolean。它还支持写入 存在 Writes[T] 的任何类型 T 的集合。

我需要指出 Node.getChildrenOf(node.id) 从数据库返回一个节点列表。因此,根据 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

我在 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转换器的整体方法很糟糕吗?

感谢并抱歉发了这么长的帖子。

【问题讨论】:

    标签: json scala intellij-idea playframework playframework-2.0


    【解决方案1】:

    问题出在"children" -> Node.getChildrenOf(node.id)Node.getChildrenOf(node.id) 返回 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)))
      )
    }
    

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

    【讨论】:

    • 这修复了它!虽然您能解释一下“(this)”的作用吗?我试过删除它,它仍然有效。
    • 如果你看一下Json.toJson的定义,你会注意到这个函数有一个额外的隐式参数。通常,该参数由编译器填写。它为toJson 函数提供了将第一个参数转换为Json 对象的实现。我用 play 2.3.10 试过一次,如果没有 (this) 添加它似乎无法编译。如果它确实适用于您的情况,我会忽略它。
    猜你喜欢
    • 2014-12-26
    • 2015-03-13
    • 2014-04-04
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多