使用Casbah / Salat定义自定义序列化 - 或将序列化委托给成员?

时间:2012-03-02 10:37:31

标签: scala mongodb casbah salat

我正在学习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()我没有收到任何错误,它只是没有效果,保存文档的方式与以往一样。对于我想要的东西来说,这似乎也有很多。

2 个答案:

答案 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