4

I need to serialize akka events to json. Based on "What JSON library to use in Scala?" I tried several libraries. Since my serializer should know nothing of all my concrete events, the events consisting of case classes and case objects should be serialised using reflection. json4s seems to match my requirements best.

class Json4sEventAdapter(system: ExtendedActorSystem) extends EventAdapter {
  implicit val formats = Serialization.formats(FullTypeHints(List(classOf[Evt])))
  override def toJournal(event: Any): Any = event match {
case e: AnyRef =>
  write(e).getBytes(Charsets.UTF_8)}

override def fromJournal(event: Any, manifest: String): EventSeq = event match {
case e: Array[Byte] => {
      EventSeq.single(read[Evt](new String(e.map(_.toChar))))}}

The problem using json4s is, that no matter which implementation is used Deserialization of objects produces different instances. Since we heavily use pattern matching for the case object this breaks all our existing code.

So my question is: which JSON library could be used with scala and akka persistence when storing case objects?

Is there even one library that handles deserialization of case objects via reflection correctly? - or does anyone have a good workaround?

Community
  • 1
  • 1
Beat Sager
  • 41
  • 2

2 Answers2

0

I can't comment on Json4s, since I've never used it, but I do know that this is a non-issue in play-json. You would do something like:

import play.api.libs.json._

sealed trait MyEventBase
case object MyEvent extends MyEventBase

implicit val myEventBaseFormat: Format[MyEventBase] = Format(Reads.StringReads.collect(ValidationError("must be the string `MyEvent`") {
  case "MyEvent" => MyEvent
}, Writes.pure("MyEvent"))

In this case, the serialization is to a bare string, and so I piggyback on the built-in StringReads to assert that the item should be deserializable to a string, and then use collect to narrow that down to the specific string. But the basic idea is that you provide the specific value you want back from deserialization in your Reads instance. Here, it's the singleton case object. So, whenever you deserialize a MyEventBase resulting in a MyEvent, you'll definitely get that same instance back.

In the real world, MyEventBase probably has other subtypes, and so you structure your Writes instance to create some form of type tag for serialization that your Reads instance can key off of to deserialize to the proper subtype. Like, you might serialize to a JSON object instead of a bare string, and that object would have a type field that identifies the subtype. Or just use something like Play JSON Extensions to automatically synthesize a reasonable Format for your sealed trait.

acjay
  • 34,571
  • 6
  • 57
  • 100
0

I highly recommend you to have a look at Stamina. It's been implemented to solve most of the usual issues you will encounter with akka-persistence.

It provides a json serialiser (based on spray-json and shapeless) which supports versioning, auto-migrating at read time as well as a testkit to ensure all older versions of persistent events are still readable.

Pascal Rodriguez
  • 981
  • 9
  • 12