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)
case class automatically extends two traits, namely Product and Serializable . Product trait is extended as case class is an algebraic data type with product type. Serializable trait is extended so that case class can be treated as a pure data - i.e capable of being serialized.
A case class has all of the functionality of a regular class, and more. When the compiler sees the case keyword in front of a class , it generates code for you, with the following benefits: Case class constructor parameters are public val fields by default, so accessor methods are generated for each parameter.
Case Classes are a representation of a data structure with the necessary methods. Functions on the data should be described in different software entities (e.g., traits, objects). Regular classes, on the contrary, link data and operations to provide the mutability.
Akka has a built-in Extension for serialization, and it is both possible to use the built-in serializers and to write your own. The serialization mechanism is both used by Akka internally to serialize messages, and available for ad-hoc serialization of whatever you might need it for.
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:
unapply
method.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With