使用json4s使用trait mixin序列化case类

时间:2014-07-25 17:22:53

标签: scala serialization case-class traits json4s

我有一个案例类Game,我可以使用json4s序列化/反序列化。

case class Game(name: String,publisher: String,website: String, gameType: GameType.Value)

在我的应用中,我使用mapperdao作为我的ORM。由于Game使用Surrogate Id我没有id,因此其部分构造函数。

但是,当mapperdao从数据库返回实体时,它会使用特征提供持久对象的id。

Game with SurrogateIntId

特质的代码是

trait SurrogateIntId extends DeclaredIds[Int]
{
    def id: Int
}

trait DeclaredIds[ID] extends Persisted

trait Persisted
{
    @transient
    private var mapperDaoVM: ValuesMap = null
    @transient
    private var mapperDaoDetails: PersistedDetails = null
private[mapperdao] def mapperDaoPersistedDetails = mapperDaoDetails

private[mapperdao] def mapperDaoValuesMap = mapperDaoVM

private[mapperdao] def mapperDaoInit(vm: ValuesMap, details: PersistedDetails) {
    mapperDaoVM = vm
    mapperDaoDetails = details
}
.....
}

当我尝试序列化Game with SurrogateIntId时,我得到空括号,我认为这是因为json4s不知道如何处理附加的特征。

我需要一种方法来序列化game,只有id添加到其属性,对于任何T with SurrogateIntId来说几乎同样重要的是这样做我将这些用于所有域对象。

任何人都可以帮助我吗?

1 个答案:

答案 0 :(得分:1)

所以这是一个非常具体的解决方案,因为我的问题的起源来自mapperDao返回DO的方式,但是由于我在json4s中钻研custom serializers,因此它可能对一般用途有帮助。

关于此问题的完整讨论可以是found on the mapperDao google group

首先,我发现在任何持久化实体(从mapperDao返回)上调用copy()都会返回我的DO的干净副本(只是case类) - 然后可以通过json4s进行序列化。但是,我不想记得在任何时候我想要序列化DO或处理映射列表等时调用copy(),因为这会很笨拙且容易出错。

因此,我创建了一个CustomSerializer包装返回的Entity(案例类DO + traits作为对象),并使用隐式清单从泛型类型中收集类。使用这种方法,然后模式匹配我的域对象以确定传入的内容,然后使用Extraction.decompose(myDO.copy())来序列化并返回干净的DO。

// Entity[Int, Persisted, Class[T]] is how my DOs are returned by mapperDao

class EntitySerializer[T: Manifest] extends CustomSerializer[Entity[Int, Persisted, Class[T]]](formats =>(
  {PartialFunction.empty} //This PF is for extracting from JSON and not needed
,{
  case g: Game => //Each type is one of my DOs
    implicit val formats: Formats = DefaultFormats //include primitive formats for serialization
    Extraction.decompose(g.copy()) //get plain DO and then serialize with json4s
  case u : User =>
    implicit val formats: Formats = DefaultFormats + new LinkObjectEntitySerializer //See below for explanation on LinkObject
    Extraction.decompose(u.copy())
  case t : Team =>
    implicit val formats: Formats = DefaultFormats + new LinkObjectEntitySerializer
    Extraction.decompose(t.copy())
...
}

对于单独的序列化程序的唯一需求是,如果您将非基元作为序列化的案例类的参数,则因为序列化程序无法使用自身进行序列化。在这种情况下,您为每个基本类(仅具有基元的IE)创建一个序列化程序,然后将其包含到具有依赖于这些基本类的对象的下一个序列化程序中。

class LinkObjectEntitySerializer[T: Manifest] extends CustomSerializer[Entity[Int, Persisted, Class[T]]](formats =>(
  {PartialFunction.empty},{
         //Team and User have Set[TeamUser] parameters, need to define this "dependency"
         //so it can be included in formats
  case tu: TeamUser => 
    implicit val formats: Formats = DefaultFormats
    ("Team" ->                     //Using custom-built representation of object
      ("name" -> tu.team.name) ~
      ("id" -> tu.team.id) ~
      ("resource" -> "/team/") ~
      ("isCaptain" -> tu.isCaptain)) ~
    ("User" ->
      ("name" -> tu.user.globalHandle) ~
      ("id" -> tu.user.id) ~
      ("resource" -> "/user/") ~
      ("isCaptain" -> tu.isCaptain))
}
  ))

这种解决方案难以令人满意。最终我需要替换mapperDao或json4s(或两者)来找到更简单的解决方案。但是,就目前而言,它似乎是具有最少开销的修复。