将 Json 读入 Scalaz 树
read Json into Scalaz Tree
这里是 Scala 新手。
我使用Play 提供了一个json API 用于读写类似目录的结构。因此我使用Scalaz.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
这是否意味着我必须在有 Tree of Nothing 的情况下进行模式匹配?我应该怎么做才好?
感谢任何帮助!发送!
我假设 apply _
实际上是 File.apply _
,这在这里不起作用。 File.apply
接受caseclassFile
的参数(其中有3个)。使用 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)
)
}
}
}
这里是 Scala 新手。
我使用Play 提供了一个json API 用于读写类似目录的结构。因此我使用Scalaz.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
这是否意味着我必须在有 Tree of Nothing 的情况下进行模式匹配?我应该怎么做才好?
感谢任何帮助!发送!
我假设 apply _
实际上是 File.apply _
,这在这里不起作用。 File.apply
接受caseclassFile
的参数(其中有3个)。使用 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)
)
}
}
}