scalaakkaupickle

Is it possible to write a upickle Serializer for akka


I would like to implement an akka Serializer using upickle but I'm not sure its possible. To do so I would need to implement a Serializer something like the following:

import akka.serialization.Serializer
import upickle.default._

class UpickleSerializer extends Serializer {

    def includeManifest: Boolean = true
    def identifier = 1234567

    def toBinary(obj: AnyRef): Array[Byte] = {
        writeBinary(obj) // ???
    }

    def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = {
        readBinary(bytes) // ???
    }
}

The problem is I cannot call writeBinary/readBinary without having the relevant Writer/Reader. Is there a way I can look these up based on the object class?


Solution

  • I found a way to do it using reflection. I base the solution on the assumption that any object that needs to be serialized should have defined a ReadWriter in its companion object:

    class UpickleSerializer extends Serializer {
    
    private var map = Map[Class[_], ReadWriter[AnyRef]]()
    
    def includeManifest: Boolean = true
    def identifier = 1234567
    
    def toBinary(obj: AnyRef): Array[Byte] = {
        implicit val rw = getReadWriter(obj.getClass)
        writeBinary(obj)
    }
    
    def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = {
        implicit val rw = lookup(clazz.get)
        readBinary[AnyRef](bytes)
    }
    
    private def getReadWriter(clazz: Class[_]) = map.get(clazz) match {
        case Some(rw) => rw
        case None =>
            val rw = lookup(clazz)
            map += clazz -> rw
            rw
    }
    
    private def lookup(clazz: Class[_]) = {
        import scala.reflect.runtime._
        val rootMirror = universe.runtimeMirror(clazz.getClassLoader)
        val classSymbol = rootMirror.classSymbol(clazz)
        val moduleSymbol = classSymbol.companion.asModule
        val moduleMirror = rootMirror.reflectModule(moduleSymbol)
        val instanceMirror = rootMirror.reflect(moduleMirror.instance)
        val members = instanceMirror.symbol.typeSignature.members
        members.find(_.typeSignature <:< typeOf[ReadWriter[_]]) match {
            case Some(rw) =>
                instanceMirror.reflectField(rw.asTerm).get.asInstanceOf[ReadWriter[AnyRef]]
            case None =>
                throw new RuntimeException("Not found")
        }
    }
    

    }