我正在为一个来自Rails的新项目学习Scala。我已经定义了一个类型,它将在我的许多模型中使用,基本上可以被认为是"属性"的集合。它基本上只是一个哈希图的包装器,将其大部分职责委托给它:
case class Description(attributes: Map[String, String]) {
override def hashCode: Int = attributes.hashCode
override def equals(other: Any) = other match {
case that: Description => this.attributes == that.attributes
case _ => false
}
}
所以我会定义一个带有Description
的模型类,如下所示:
case class Person(val name: String, val description: Description)
但是,当我使用 SalatDAO 坚持Person
时,我最终会得到一个如下所示的文档:
{
name : "Russell",
description:
{
attributes:
{
hair: "brown",
favourite_color: "blue"
}
}
}
当实际上我不需要在description
标签中嵌套attributes
标签时 - 我真正想要的是:
{
name : "Russell",
description:
{
hair: "brown",
favourite_color: "blue"
}
}
我没有尝试过,但我认为如果我Description
扩展一个Map
而不是包含一个,我可以让它工作,但我宁愿不这样做,因为Description
不是一种Map
,它是具有Map
的一些行为以及我稍后将添加的其他行为的东西。组合超过继承等等。
所以我的问题是,我如何告诉 Salat(或 Casbah,我实际上有点不清楚哪个在进行转换,因为我才刚刚开始使用它们(如何序列化和反序列化Description
类?在卡斯巴哈教程中,它说:
还可以创建自己的自定义类型序列化程序和 反序列化程序。请参阅自定义序列化程序和反序列化程序。
但是这个页面似乎不存在。还是我走错了路?实际上有没有一种非常简单的方法来表明这就是我想要发生的事情,注释或其他东西?或者我可以以某种方式简单地将序列化委托给属性映射吗?
编辑:在查看了JodaTime转换助手的来源后,我尝试了以下方法,但还没有运气让它工作:
import org.bson.{ BSON, Transformer }
import com.mongodb.casbah.commons.conversions.MongoConversionHelper
object RegisterCustomConversionHelpers extends Serializers
with Deserializers {
def apply() = {
super.register()
}
}
trait Serializers extends MongoConversionHelper
with DescriptionSerializer {
override def register() = {
super.register()
}
override def unregister() = {
super.unregister()
}
}
trait Deserializers extends MongoConversionHelper {
override def register() = {
super.register()
}
override def unregister() = {
super.unregister()
}
}
trait DescriptionSerializer extends MongoConversionHelper {
private val transformer = new Transformer {
def transform(o: AnyRef): AnyRef = o match {
case d: Description => d.attributes.asInstanceOf[AnyRef]
case _ => o
}
}
override def register() = {
BSON.addEncodingHook(classOf[Description], transformer)
super.register()
}
}
当我调用RegisterCustomConversionHelpers()
然后保存Person
时,我没有收到任何错误,它只是没有效果,以与以往相同的方式保存文档。这似乎也有很多事情要做,以实现我想要的东西。
Salat 维护者在这里。
我不明白描述作为包装器的价值。 它包装了一个属性映射,覆盖了案例类的默认等于和哈希码 impl - 这似乎是不必要的,因为 impl 无论如何都委托给映射,而这正是案例类所做的 - 并为序列化对象引入了额外的间接层。
您是否考虑过:
case class Person(val name: String, val description: Map[String, String])
这将完全符合您想要的开箱即用。
在另一种情况下,我会推荐一个简单的类型别名,但不幸的是,由于它们在腌制的 Scala 签名中的描述方式存在一些问题,Salat 现在无法支持类型别名。
(为了简洁起见,您可能从示例中省略了这一点,但最好的做法是 Mongo 模型有一个_id字段 - 如果您没有,Mongo Java 驱动程序将为您提供一个(
在salat-core测试包中有一个自定义BSON钩子的工作示例(它处理java.net.URL(。 可能是您的钩子无法正常工作仅仅是因为您没有在正确的位置注册它? 但是,我仍然建议删除描述,除非它增加了一些从上面的示例中不明显的值。
根据@prasinous的回答,我认为这不会那么容易,所以我将我的设计更改为以下内容,这几乎可以满足我的需求。我不是将Description
保留为字段,而是保留一个普通地图,然后将Described
特征混合到我想要描述的模型类中,这会自动在创建对象时将映射转换为Description
。如果有人能指出这种方法的任何明显问题或任何改进建议,将不胜感激。
class Description(val attributes: Map[String, String]){
//rest of class omitted
}
trait Described {
val attributes: Map[String, String]
val description = new Description(attributes)
}
case class Person(name: String, attributes: Map[String, String]) extends Described