【问题标题】:read Json into Scalaz Tree将 Json 读入 Scalaz 树
【发布时间】:2015-04-04 13:28:12
【问题描述】:

这里是 Scala 新手。

我使用 Play 提供了一个 json API 用于读取和写入类似目录的结构。因此我使用了 Scalaz.Tree,它提供了遍历、更新和重建 Tree 的方法。

将 Tree 格式化为 json 效果很好:

case class File(id: String = BSONObjectID.generate.toString(), name: String, noteBookId: String = null)
implicit val fileFormat: Format[File] = Json.format[File]

implicit def treeWrites: Writes[Tree[File]] =
new Writes[Tree[File]] {
  def writes(o: Tree[File]) = o match {
    case Node(file, children) => Json.obj(
      "name" -> file.name,
      "id" -> file.id,
      "children" -> JsArray(children.map(Json.toJson(_))),
      "notebookId" -> file.noteBookId
    )
  }
}

然而,将 json 读入树中失败

implicit def treeReads: Reads[Tree[File]] = (
  //(__ \ "children").lazyRead(Reads.seq[File](treeReads)) and
  (__ \ "children").read[Tree[File]] and
  (__ \ "name").read[String] and 
  (__ \ "notebookid").read[String] and // <-- this is line 41, where the error message points at!!
  (__ \ "id").read[String]
)(apply _)

implicit val treeFormat: Format[Tree[File]] = Format(treeReads, treeWrites)

我得到的错误:

[error] /home/dikken/Development/core-service-spaas/app/models/dirTree.scala:41: overloaded method value apply with alternatives:
[error]   [B](f: B => (scalaz.Tree[model.treedir.File], String, String, String))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] <and>
[error]   [B](f: (scalaz.Tree[model.treedir.File], String, String, String) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.Reads])play.api.libs.json.Reads[B]
[error]  cannot be applied to ((=> Nothing) => scalaz.Tree[Nothing])
[error]     (__ \ "id").read[String] and
[error]                              ^
[error] one error found
[error] (compile:compile) Compilation failed

这是否意味着我必须在我有无树的情况下进行模式匹配?我应该怎么做呢?

任何帮助表示赞赏!发送!

【问题讨论】:

    标签: scala playframework tree playframework-2.0 scalaz


    【解决方案1】:

    我将假设apply _ 实际上是File.apply _,它在这里不能工作。 File.apply接受case类File的参数(一共有三个)。使用 JSON 组合器,它试图将上面的四个参数传递给File.apply,它不会混合。它不会产生Tree[File]。您需要做的是将File.apply 替换为接受(children、notebookid、name、id)作为参数的方法,并生成Tree[File]

    这是一个有点粗略的方法:

    def jsonToTree(children: Seq[Tree[File]], name: String, notebookid: String, id: String): Tree[File] =
        Tree.node(File(id, name, notebookid), children.toStream)
    

    Reads 现在看起来更像这样:

    implicit def treeReads: Reads[Tree[File]] = (
      (__ \ "children").lazyRead[Seq[Tree[File]]](Reads.seq(treeReads)).orElse(Reads.pure(Nil)) and
      (__ \ "name").read[String] and 
      (__ \ "notebookid").read[String] and
      (__ \ "id").read[String]
    )(jsonToTree _)
    

    您也更接近注释掉的行。因为这是一个递归结构,所以我们需要使用lazyRead

    测试:

    val js = Json.parse("""{
        "id": "1",
        "name": "test",
        "notebookid": "abc",
        "children": [
            {
                "id": "2",
                "name": "test222",
                "notebookid": "ijk"
            },
            {
                "id": "3",
                "name": "test333",
                "notebookid": "xyz"
            }
        ]
    }""")
    
    scala> val tree = js.as[Tree[File]]
    tree: scalaz.Tree[File] = <tree>
    
    scala> tree.rootLabel
    res8: File = File(1,test,abc)
    
    scala> tree.subForest
    res9: Stream[scalaz.Tree[File]] = Stream(<tree>, ?)
    

    这也可以在没有组合符的情况下完成(当然以不同的方式),也可以(假设有一个隐含的Reads[File] 可用):

    implicit def treeReads: Reads[Tree[File]] = new Reads[Tree[File]] {
        def reads(js: JsValue): JsResult[Tree[File]] = {
            js.validate[File] map { case file =>
                (js \ "children").validate[Stream[Tree[File]]].fold(
                    _        => Tree.leaf(file),
                    children => Tree.node(file, children)
                )
            } 
        }
    }
    

    【讨论】:

    • 发送!这将需要很长时间才能弄清楚自己
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 2019-08-06
    相关资源
    最近更新 更多