有没有人知道如何使用 subset2 将DBObject解析为案例类对象?超简洁的文档对我没有帮助:(
考虑以下案例类
case class MenuItem(id : Int, name: String, desc: Option[String], prices: Option[Array[String]], subitems: Option[Array[MenuItem]])
object MenuItem {
implicit val asBson = BsonWritable[MenuItem](item =>
{
val buf: DBObjectBuffer = DBO("id" -> item.id, "name" -> item.name)
item.desc match { case Some(value) => buf.append("desc" -> value) case None => }
item.prices match { case Some(value) => buf.append("prices" -> value) case None => }
item.subitems match { case Some(value) => buf.append("subitems" -> value) case None => }
buf()
}
)
}
我写了这个解析器
val menuItemParser: DocParser[MenuItem] = int("id") ~ str("name") ~ str("desc").opt ~ get[Array[String]]("prices").opt ~ get[Array[MenuItem]]("subitems").opt map {
case id ~ name ~ desc_opt ~ prices_opt ~ subitems => {
MenuItem(id, name, desc_opt, prices_opt, subitems)
}
}
如果删除最后一个字段subitems
,则有效。但上面显示的版本无法编译,因为MenuItem
具有引用自身的字段。它给了我以下错误
Cannot find Field for Array[com.borsch.model.MenuItem]
val menuItemParser: DocParser[MenuItem] = int("id") ~ str("name") ~ str("desc").opt ~ get[Array[String]]("prices").opt ~ get[Array[MenuItem]]("subitems").opt map {
^
它显然无法编译,因为上一个get
想要隐含Field[MenuItem]
。但是,如果我为MenuItem
定义它,那么DocParser[MenuItem]
的复制粘贴是不是很多?
你会如何优雅地做到这一点?
答案 0 :(得分:2)
我是Subset的作者(包括1.x和2)。
README表示您需要为每个Field[T]
提供T
您想要阅读的内容(在“反序列化”部分下)
只是旁注。坦率地说,我认为将
MenuItem
的解串器命名为jodaDateTime
并不合理。
无论如何Field[T]
必须从香草BSON类型转换为T
。 BSON无法本地存储MenuItem
,请参阅本机BSON类型here
但当然主要的问题是你有一个递归数据结构,所以你的“序列化器”(BsonWritable
)和“反序列化器”(Field
)也必须是递归的。 子集具有List[T]
的隐式序列化程序/反序列化程序,但它们要求您提供MenuItem
:递归。
为了简短起见,我将向您展示如何为更简单的“案例类”编写类似的内容。
假设我们有
case class Rec(id: Int, children: Option[List[Rec]])
然后作者可能看起来像
object Rec {
implicit object asBson extends BsonWritable[Rec] {
override def apply(rec: Rec) =
Some( DBO("id" -> rec.id, "children" -> rec.children)() )
}
此处,当您将rec.children
写入“DBObject”时,正在使用BsonWriteable[Rec]
,并且它依次需要“隐式”Field[Rec]
。因此,此序列化程序 是递归的。
从解串器开始,以下将执行
import DocParser._
implicit lazy val recField = Field({ case Doc(rec) => rec })
lazy val Doc: DocParser[Rec] =
get[Int]("id") ~ get[List[Rec]]("children").opt map {
case id ~ children => new Rec(id, children)
}
}
这些是相互递归的(记得使用lazy val
!)
你会像这样使用它们:
val dbo = DBO("y" -> Rec(123, Some(Rec(234, None) :: Nil))) ()
val Y = DocParser.get[Rec]("y")
dbo match {
case Y(doc) => doc
}