This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
) |
Since I work with the Battlefield franchise let’s create some domain classes that we are going to serialize and deserialize.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
The first candidate will be Scala Pickling. The following code pickles a List of 3000 random WeaponAccessory instances.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
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!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |