Two Scala Serialization Examples

In the last two days I’ve been looking into ways to serialize and deserialize some Scala objects. I tested a few suggestions that were mentioned on this post on Stackoverflow. As a reference for myself (and because sometimes it is hard to find good examples) I am adding two examples for Scala Pickling and Twitter Chill. Let’s have a basic SBT project first.

import sbt._
import sbt.Keys._
organization := "me"
name := "scala-ser"
version := "0.1-SNAPSHOT"
scalaVersion := "2.10.4"
resolvers += Resolver.sonatypeRepo("snapshots")
libraryDependencies ++= Seq(
"com.twitter" %% "chill" % "0.3.6",
"org.scalamacros" % "quasiquotes_2.10.4" % "2.0.0-M6",
"org.scala-lang" %% "scala-pickling" % "0.8.0-SNAPSHOT"
)
view raw build.sbt hosted with ❤ by GitHub


Since I work with the Battlefield franchise let’s create some domain classes that we are going to serialize and deserialize.

package me
import java.util.UUID
import scala.util.Random
class WeaponAccessory(
var guid: String,
var slug: Option[String],
var nameSID: Option[String],
var descriptionSID: Option[String],
var categorySID: Option[String],
var requirements: Option[List[UnlockRequirement]],
var weaponData: Option[WeaponData]) {
def this() = this("", None, None, None, None, None, None)
}
class UnlockRequirement(
var requirementType: RequirementType,
var valueNeeded: Float,
var codeNeeded: String) {
def this() = this(Bucket, 0.0F, "")
}
class WeaponData(
var statDamage: Option[Float],
var statAccuracy: Option[Float],
var statMobility: Option[Float],
var statRange: Option[Float],
var statHandling: Option[Float]) {
def this() = this(None, None, None, None, None)
}
sealed trait RequirementType {
def name() = getClass.getSimpleName
}
object Bucket extends RequirementType
object Create {
def randomWeaponAccessory() = {
val accessory = new WeaponAccessory()
accessory.guid = "%s".format(UUID.randomUUID())
accessory.slug = randomOption(randString(10))
accessory.nameSID = randomOption(randString(25))
accessory.descriptionSID = randomOption(randString(25))
accessory.categorySID = randomOption(randString(25))
accessory.requirements = Some(
List.fill(Random.nextInt(5))(randomRequirement())
)
accessory.weaponData = randomOption(randomWeaponData())
accessory
}
private def randomRequirement() = {
val requirement = new UnlockRequirement()
requirement.requirementType = Bucket
requirement.codeNeeded = randString(5)
requirement.valueNeeded = Random.nextFloat()
requirement
}
private def randomWeaponData() = {
val weaponData = new WeaponData()
weaponData.statAccuracy = randomOption(Random.nextFloat())
weaponData.statDamage = randomOption(Random.nextFloat())
weaponData.statHandling = randomOption(Random.nextFloat())
weaponData.statMobility = randomOption(Random.nextFloat())
weaponData.statRange = randomOption(Random.nextFloat())
weaponData
}
private def randomOption[T](value: T) = {
if (Random.nextBoolean()) {
Some(value)
} else {
None
}
}
private def randString(x: Int) = Random.alphanumeric.take(x).mkString
}
view raw Items.scala hosted with ❤ by GitHub


The first candidate will be Scala Pickling. The following code pickles a List of 3000 random WeaponAccessory instances.

package me
import scala.pickling._
import json._
object Pickling {
def main(args: Array[String]) {
val items = List.fill(3000)(Create.randomWeaponAccessory())
val pckl = items.pickle
val json = pckl.value
val jsonPckl = new JSONPickle(json)
val deser = jsonPckl.unpickle[List[WeaponAccessory]]
assert(deser.size == 3000)
}
}
view raw Pickling.scala hosted with ❤ by GitHub


Unfortunately the code doesn't even compile properly. Scala Pickling uses Macros and advanced Scala compile features. Trying to compile Pickling.scala fails during compilation. Also people are encouraged to depend on a SNAPSHOT version which means you are always depending on the latest patches. When I wrote this blog post I hit this issue. Verdict: scala-pickling is very easy to use and works great for very simple stuff. As soon as your object graph gets a bit more complicated you will hit weird errors. Another problem is the lack of a non-SNAPSHOT version.

The seconds test candidate was Twitter Chill which is based on Kryo. chill-scala adds some Scala specific extensions. Your SBT project should depend on chill directly, which contains the code in chill-scala (which isn’t published separately). Even though they don’t have Scala examples in their Github documentation and I got some cryptic errors first when doing stuff wrong - I have to say this is an awesome library that works great! Also the authors reply fast on Twitter. Verdict: highly recommended!

package me
import com.twitter.chill.ScalaKryoInstantiator
import java.io.ByteArrayOutputStream
import com.esotericsoftware.kryo.io.{Input, Output}
object Chill {
def main(args: Array[String]) {
val items = List.fill(3000)(Create.randomWeaponAccessory())
val instantiator = new ScalaKryoInstantiator
instantiator.setRegistrationRequired(false)
val kryo = instantiator.newKryo()
val baos = new ByteArrayOutputStream
val output = new Output(baos, 4096)
kryo.writeObject(output, items)
val input = new Input(baos.toByteArray)
val deser = kryo.readObject(input, classOf[List[WeaponAccessory]])
assert(deser.size == 3000)
}
}
view raw Chill.scala hosted with ❤ by GitHub