我正在学习Scala,以获得来自Rails的新项目。我已经定义了一个将在我的模型中使用的类型,它基本上可以被认为是“属性”的集合。它基本上只是一个hashmap的包装器,它将大部分职责委托给它:
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"
}
}
}
实际上,我不需要在attributes
标记中嵌套description
标记 - 我真正想要的是:
{
name : "Russell",
description:
{
hair: "brown",
favourite_color: "blue"
}
}
我没有尝试过,但我认为如果我Description
延长Map
而不是包含一个Description
,我可以让它工作,但我宁愿不这样做,因为{{1} }}不是Map
的一种类型,它具有Map
的某些行为以及我自己稍后要添加的其他行为。继承的构成等。
所以我的问题是,我怎么能告诉Salat(或Casbah,我实际上有点不清楚哪个是转换,因为我刚开始使用它们)如何序列化和反序列化{{1类?在casbah教程here中它说:
也可以创建自己的自定义类型序列化器和 解串器。请参阅自定义序列化和反序列化。
但是这个页面似乎不存在。或者我是以错误的方式去做的?实际上是否有一种非常简单的方法来表明这是我想要发生的事情,注释还是什么?或者我可以简单地以某种方式将序列化委托给属性映射吗?
编辑:在看了JodaTime转换助手的来源之后,我尝试了以下但是没有运气让它继续工作:Description
当我致电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()
我没有收到任何错误,它只是没有效果,保存文档的方式与以往一样。对于我想要的东西来说,这似乎也有很多。
答案 0 :(得分:4)
Salat维护者在这里。
我不理解Description作为包装器的值。它包装了一个属性映射,覆盖了一个case类的默认equals和hashcode impl - 这似乎是不必要的,因为impl无论如何都被委托给了map,而这正是case类所做的 - 而且引入了一个额外的间接层序列化对象。
您是否考虑过:
case class Person(val name: String, val description: Map[String, String])
这将完全符合您的要求。
在另一种情况下,我会推荐一个简单的类型别名,但遗憾的是Salat现在不能支持类型别名,因为它们在腌制Scala签名中的描述有些问题。
(你可能从简洁的例子中省略了这个,但最好的做法是你的Mongo模型有一个_id字段 - 如果不这样做,Mongo Java驱动程序会为你提供一个)
salat-core测试包中有一个自定义BSON钩子的工作示例(它处理java.net.URL)。可能是因为你没有在正确的地方注册它而你的钩子不能正常工作?但是,我仍然建议删除描述,除非它添加了一些上述示例中不明显的值。
答案 1 :(得分:0)
根据@prasinous的回答我决定这不会那么容易,所以我把设计改为以下几点,这几乎让我得到了我想要的东西。我没有将Description
作为一个字段持久化,而是坚持一个vanilla地图,然后将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