有效地序列化案例类 [英] Efficiently serializing case classes

查看:48
本文介绍了有效地序列化案例类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我正在开发的库,我需要提供一种高效、方便且类型安全的方法来序列化 Scala 类.理想的情况是,如果用户可以创建一个案例类,并且只要所有成员都是可序列化的,那么它也应该是无缝的.我在序列化和反序列化阶段都准确地知道类型,因此不需要(也负担不起)将任何模式"信息作为序列化格式的一部分(如 Java 对象序列化).

我一直在尝试一些想法,这个想法似乎非常接近.我在这里看到的主要问题是用户必须如何指定类的应用"和取消应用"功能.由于这些实际上是静态函数,我想知道是否有可能让编译器找到它.

这是一个自包含的例子:

trait InOut[T] {//只是保持简单,用于说明目的def toWire(x: T): 数组[字节]def fromWire(v: Array[Byte]): T}对象 InOutConversions {//假设这些被正确实现隐式 def Int = new InOut[Int] {def toWire(x: Int): Array[Byte] = Array[Byte]()def fromWire(v: Array[Byte]): Int = 44}隐式 def String = new InOut[String] {def toWire(x: String): Array[Byte] = Array[Byte]()def fromWire(v: Array[Byte]): String = "blah"}//等等...对于所有基本类型}

然后我需要一个这样的函数:

def serialize2[T, A1 : InOut, A2 : InOut](unapply : T => Option[Product2[A1, A2]])(obj : T) : Array[Byte] = {val product : Product2[A1, A2] = unapply(obj).get隐式[InOut[A1]].toWire(product._1)++ 隐式[InOut[A2]].toWire(product._2)}

这将使用户可以非常轻松地使用它.例如

case class Jesus(a: Int, b: String)val j = Jesus(4, "测试")serialize2 (Jesus.unapply(_)) (j)

但是正如你所看到的,最后一行真的很糟糕.肯定有可能改进吗?(给个耶稣,我肯定能找到'unapply'静态方法)

解决方案

因为没有通用的方法来获取给定案例类的伴随对象,您将添加来做一些额外的工作.我看到三个选项:

  1. 您可以使用结构化类型,但会降低性能
  2. 您可以使用一个小类型类来隐式解析正确的 unapply 方法.
  3. 您可以向案例类实例添加 toTuple 方法.

例如:

trait ToTuple2[A1,A2] {def toTuple: (A1,A2)}case class Jesus(a: Int, b: String) extends ToTuple2[Int,String] {val toTuple = (a,b)}def serialize2[T <: ToTuple2[A1,A2], A1 : InOut, A2 : InOut](obj : T): Array[Byte] = {val 产品:Product2[A1, A2] = obj.toTuple隐式[InOut[A1]].toWire(product._1)++ 隐式[InOut[A2]].toWire(product._2)}

<小时>

选项 2 的代码示例:

case class Jesus(a: Int, b: String)特质 Unapply2[T,A1,A2] {def asTuple( t: T ): (A1,A2)}隐式 val UnapJesus = new Unapply2[Jesus,Int,String] {def asTuple( j: Jesus ) = Jesus.unapply(j).get}def serialize2[T, A1, A2](obj : T)(隐式 unap: Unapply2[T,A1,A2], inout1: InOut[A1], inout2: InOut[A2]) : Array[Byte] = {val product : Product2[A1, A2] = unap.asTuple(obj)inout1.toWire(product._1) ++ inout2.toWire(product._2)}

您应该看看 SBinary,它看起来很相似.>

For a library I'm working on, I need to provide an efficient, convenient and typesafe method of serializing scala classes. The ideal would be if a user can create a case class, and as long as all the members are serializable it should seemlessly be so too. I know precisely the type during both the serializing and deserializing stage so there's no need (and can not afford to) have any "schema" information as part of the seriazation format (like the Java object serialisation).

I've been playing with a few ideas, and this one seems to come pretty close. The major problem I see here, is how the user has to specify class's "apply" and "unapply" function. Since these are really static functions, I'm wondering if it's possible to get the compiler to find it.

Here's a self contained example:

trait InOut[T] {
  // just keeping things simple, for illustration purposes
  def toWire(x: T): Array[Byte]
  def fromWire(v: Array[Byte] ): T
}

object InOutConversions {
  // Pretend these are implemented properly

    implicit def Int = new InOut[Int] {
      def toWire(x: Int): Array[Byte] = Array[Byte]()
      def fromWire(v: Array[Byte] ): Int = 44
    }

    implicit def String = new InOut[String] {
      def toWire(x: String): Array[Byte] = Array[Byte]()
      def fromWire(v: Array[Byte] ): String = "blah"
    }

    // etc... for all the basic types
}

And then I need a function like this:

def serialize2[T, A1 : InOut, A2 : InOut](unapply : T => Option[Product2[A1, A2]])(obj : T) : Array[Byte] = {
  val product : Product2[A1, A2] = unapply(obj).get 
   implicitly[InOut[A1]].toWire(product._1) ++ implicitly[InOut[A2]].toWire(product._2)
}

Which would allow a user to use it pretty easy. e.g.

case class Jesus(a: Int, b: String)
val j = Jesus(4, "Testing")
serialize2 (Jesus.unapply(_)) (j)

But as you can see, that last line was really quite yuck. Surely it must be possible to improve on that? (Given a Jesus, surely I can find the 'unapply' static method)

解决方案

Because don't have a generic way of getting the companion object of a given case class, you will add to do some extra work. I see three options:

  1. You can either use structural typing, but you will loose performance
  2. You can use a small type class to resolve implicitly the correct unapply method.
  3. You can add a toTuple method to the case class instances.

For instance:

trait ToTuple2[A1,A2] {
  def toTuple: (A1,A2)
}

case class Jesus(a: Int, b: String) extends ToTuple2[Int,String] {
  val toTuple = (a,b)
}

def serialize2[T <: ToTuple2[A1,A2], A1 : InOut, A2 : InOut](obj : T): Array[Byte] = {
  val product : Product2[A1, A2] = obj.toTuple
  implicitly[InOut[A1]].toWire(product._1) ++ implicitly[InOut[A2]].toWire(product._2)
}


Code example for option 2:

case class Jesus(a: Int, b: String)

trait Unapply2[T,A1,A2] {
  def asTuple( t: T ): (A1,A2)
}

implicit val UnapJesus = new Unapply2[Jesus,Int,String] {
  def asTuple( j: Jesus ) = Jesus.unapply(j).get
}

def serialize2[T, A1, A2](obj : T)
(implicit unap: Unapply2[T,A1,A2], inout1: InOut[A1], inout2: InOut[A2])  : Array[Byte] = {
  val product : Product2[A1, A2] = unap.asTuple(obj)
  inout1.toWire(product._1) ++ inout2.toWire(product._2)
}

You should have a look at SBinary, it looks similar.

这篇关于有效地序列化案例类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆