SandroGrzicic / ScalaBuff

the scala protocol buffers (protobuf) compiler
Other
219 stars 80 forks source link

Support for Spray #72

Open hbf opened 11 years ago

hbf commented 11 years ago

I am trying to use ScalaBuff in a Spray application. The marshalling of messages to Protobuf works like a charm using ScalaBuff:

object ProtobufProtocols {
  val ProtobufMediaType = MediaTypes.register(MediaType.custom("application", "protobuf", compressible = false, binary = true))
  val Protobuf = ContentType(ProtobufMediaType)

  implicit def marshaller[T <: com.google.protobuf.MessageLite]() =
    Marshaller.of[T](Protobuf) { (value, contentType, ctx) ⇒
        ctx.marshalTo(HttpEntity(contentType, value.toByteArray))
    }
}

This is generic and works for all messages generated by ScalaBuff.

I am, however, not sure how to get unmarshalling:

implicit def unmarshaller[T <: net.sandrogrzicic.scalabuff.Message]() =
  Unmarshaller[T](ProtobufMediaType) {
    case HttpBody(contentType, buffer) =>
      // TODO
  }

In this method, I somehow need to call T's parseFrom method.

Does ScalaBuff provide a way to achieve this?

Note: I also posted this question in the Spray User Group.

SandroGrzicic commented 11 years ago

After some random searching, perhaps you can try something like this:

implicit def unmarshaller[T <: net.sandrogrzicic.scalabuff.Message]()(implicit tag: scala.reflect.ClassTag[T]) =
  Unmarshaller[T](ProtobufMediaType) {
    case HttpBody(contentType, buffer) =>
      tag.erasure.newInstance.asInstanceOf[T].parseFrom(buffer)
  }

Haven't tried it and it's by very definition unsafe, however every ScalaBuff case class comes with a no-arg constructor (every parameter is optional) so in theory this should never throw an Exception. If you pass in a Message that doesn't have a no-arg constructor, it will throw an InstantiationException.

Also, there's probably a cleaner way to do this - but I hope it's a good start. :)

viktorklang commented 11 years ago

https://github.com/akka/akka/blob/master/akka-remote/src/main/scala/akka/remote/serialization/ProtobufSerializer.scala

SandroGrzicic commented 11 years ago

That works if you have a Class[T], but I don't think you do with Spray's Unmarshaller.

However I'm guessing it's better to do it this way, with a direct parseFrom method call on the object of type T instead of instantiating the whole class. I'm not sure about the exact semantics though since the companion object is a, well, object.