[英]Define custom serialization with Casbah / Salat - or delegate serialization to member?
I'm in the process of learning Scala for a new project having come from Rails. 我正在学习Scala,以获得来自Rails的新项目。 I've defined a type that is going to be used in a number of my models which can basically be thought of as collection of 'attributes'.
我已经定义了一个将在我的模型中使用的类型,它基本上可以被认为是“属性”的集合。 It's basically just a wrapper for a hashmap that delegates most of its responsibilities to it:
它基本上只是一个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
}
}
So I would then define a model class with a Description
, something like: 因此,我将使用
Description
定义一个模型类,如:
case class Person(val name: String, val description: Description)
However, when I persist a Person
with a SalatDAO I end up with a document that looks like this: 但是,当我坚持使用SalatDAO的
Person
,我最终得到的文档看起来像这样:
{
name : "Russell",
description:
{
attributes:
{
hair: "brown",
favourite_color: "blue"
}
}
}
When in actual fact I don't need the nesting of the attributes
tag in the description
tag - what I actually want is this: 实际上我不需要在
description
标签中嵌套attributes
标签 - 我真正想要的是:
{
name : "Russell",
description:
{
hair: "brown",
favourite_color: "blue"
}
}
I haven't tried, but I reckon I could get that to work if I made Description
extend a Map
rather than contain one, but I'd rather not, because a Description
isn't a type of Map
, it's something which has some of the behaviour of a Map
as well as other behaviour of its own I'm going to add later. 我没有尝试过,但我认为如果我使
Description
扩展一个Map
而不是包含一个,我可以得到它,但我宁愿不这样做,因为一个Description
不是一种Map
,它有一些东西有一些我将在稍后添加一个Map
行为以及它自己的其他行为。 Composition over inheritance and so on. 继承的构成等。
So my question is, how can I tell Salat (or Casbah, I'm actually a bit unclear as to which is doing the conversion as I've only just started using them) how to serialize and deserialize the Description
class? 所以我的问题是,我怎么能告诉Salat(或Casbah,我实际上有点不清楚哪些是转换,因为我刚开始使用它们)如何序列化和反序列化
Description
类? In the casbah tutorial here it says: 在这里的casbah教程中它说:
It is also possible to create your own custom type serializers and deserializers.
也可以创建自己的自定义类型序列化器和反序列化器。 See Custom Serializers and Deserializers.
请参阅自定义序列化和反序列化。
But this page doesn't seem to exist. 但是这个页面似乎不存在。 Or am I going about it the wrong way?
或者我是以错误的方式去做的? Is there actually a really simple way to indicate this is what I want to happen, an annotation or something?
实际上是否有一种非常简单的方法来表明这是我想要发生的事情,注释还是什么? Or can I simply delegate the serialization to the attributes map in some way?
或者我可以简单地以某种方式将序列化委托给属性映射?
EDIT: After having a look at the source for the JodaTime conversion helper I've tried the following but have had no luck getting it to work yet: 编辑:看了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()
}
}
When I call RegisterCustomConversionHelpers()
then save a Person
I don't get any errors, it just has no effect, saving the document the same way as ever. 当我调用
RegisterCustomConversionHelpers()
然后保存一个Person
我没有得到任何错误,它只是没有效果,以同样的方式保存文档。 This also seems like quite a lot to have to do for what I want. 对于我想要的东西,这似乎也是很多。
Salat maintainer here. Salat维护者在这里。
I don't understand the value of Description as a wrapper here. 我不理解Description作为包装器的值。 It wraps a map of attributes, overrides the default equals and hashcode impl of a case class - which seems unnecessary since the impl is delegated to the map anyhow and that is exactly what the case class does anyway - and introduces an additional layer of indirection to the serialized object.
它包装了一个属性映射,覆盖了一个case类的默认equals和hashcode impl - 这似乎是不必要的,因为impl无论如何都被委托给了map,而这正是case类所做的 - 并引入了一个额外的间接层序列化对象。
Have you considered just: 你考虑过:
case class Person(val name: String, val description: Map[String, String])
This will do exactly what you want out of box. 这将完全符合您的要求。
In another situation I would recommend a simple type alias but unfortunately Salat can't support type aliases right now due to some issues with how they are depicted in pickled Scala signatures. 在另一种情况下,我会推荐一个简单的类型别名,但不幸的是Salat现在不能支持类型别名,因为它们在腌制的Scala签名中描述了它们的一些问题。
(You probably omitted this from your example from brevity, but it is best practice for your Mongo model to have an _id field - if you don't, the Mongo Java driver will supply one for you) (你可能从简洁的例子中省略了这个,但最好的做法是你的Mongo模型有一个_id字段 - 如果不这样做,Mongo Java驱动程序会为你提供一个)
There is a working example of a custom BSON hook in the salat-core test package (it handles java.net.URL). 在salat-core测试包中有一个自定义BSON钩子的工作示例(它处理java.net.URL)。 It could be that your hook is not working simply because you are not registering it in the right place?
可能是因为你没有在正确的地方注册它而你的钩子不能正常工作? But still, I would recommend getting rid of Description unless it is adding some value that is not evident from your example above.
但是,我仍然建议删除描述,除非它添加一些从上面的示例中不明显的值。
Based on @prasinous' answer I decided this wasn't going to be that easy so I've changed my design a bit to the following, which pretty much gets me what I want. 根据@prasinous的回答,我决定这不会那么容易,所以我把设计改为以下,这几乎让我得到了我想要的东西。 Rather than persisting the
Description
as a field I persist a vanilla map then mix in a Described
trait to the model classes I want to have a description, which automatically converts the map to Description
when the object is created. 我没有将
Description
作为字段持久化,而是持久保存了一个vanilla地图,然后将Described
特征混合到我想要Described
的模型类中,该Description
在创建对象时自动将地图转换为Description
。 Would appreciate it if anyone can point out any obvious problems to this approach or any suggestions for improvement. 如果有人能指出这种方法的任何明显问题或任何改进建议,将不胜感激。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.