3

我正在为一个来自 Rails 的新项目学习 Scala。我已经定义了一个将在我的许多模型中使用的类型,基本上可以将其视为“属性”的集合。它基本上只是一个 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
  }
}

所以我会用 a 定义一个模型类Description,比如:

case class Person(val name: String, val description: Description)

但是,当我坚持Person使用 SalatDAO 时,我最终会得到一个如下所示的文档:

{
  name : "Russell",
  description: 
  {
    attributes: 
    {
      hair: "brown",
      favourite_color: "blue"
    }
  }
}

实际上我不需要在attributes标签中嵌套description标签 - 我真正想要的是:

{
  name : "Russell",
  description: 
  {
    hair: "brown",
    favourite_color: "blue"
  }
}

我没有尝试过,但我认为如果我Description扩展 aMap而不是包含一个,我可以让它工作,但我宁愿不这样做,因为 aDescription不是 的类型Map,它具有一些行为aMap以及它自己的其他行为,我稍后会添加。组合优于继承等等。

所以我的问题是,我如何告诉 Salat(或 Casbah,实际上我有点不清楚是哪个在进行转换,因为我才刚刚开始使用它们)如何序列化和反序列化Description类?在这里的 casbah 教程中说:

也可以创建自己的自定义类型序列化器和反序列化器。请参阅自定义序列化器和反序列化器。

但是这个页面似乎不存在。还是我走错了路?实际上有没有一种非常简单的方法来表明这是我想要发生的事情,注释或其他东西?或者我可以以某种方式简单地将序列化委托给属性映射吗?

编辑:在查看了 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我没有收到任何错误,它只是没有效果,以与以往相同的方式保存文档。对于我想要的东西,这似乎也有很多事情要做。

4

2 回答 2

4

Salat维护者在这里。

我不明白 Description 作为包装器的价值。它包装了一个属性映射,覆盖了案例类的默认 equals 和哈希码 impl - 这似乎没有必要,因为无论如何 impl 都被委托给了映射,而这正是案例类所做的事情 - 并引入了一个额外的间接层序列化的对象。

您是否考虑过:

case class Person(val name: String, val description: Map[String, String])

这将完全满足您的要求。

在另一种情况下,我会推荐一个简单的类型别名,但不幸的是 Salat 目前无法支持类型别名,因为它们在腌制的 Scala 签名中的描述方式存在一些问题。

(您可能从简洁的示例中省略了这一点,但您的 Mongo 模型最好有一个 _id 字段 - 如果您没有,Mongo Java 驱动程序将为您提供一个)

在 salat-core 测试包中有一个自定义 BSON 挂钩的工作示例(它处理 java.net.URL)。可能是因为您没有在正确的位置注册它,所以您的钩子无法正常工作?但是,我仍然建议摆脱 Description ,除非它增加了一些在上面的示例中不明显的价值。

于 2012-03-02T13:26:51.003 回答
0

根据@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
于 2012-03-02T16:05:27.203 回答