Julias
Julias

Reputation: 5892

How to setup implicit json convertion for spray+akka actor

I'm Java developer and pretty new to scala.

I'm implementing some rest API that use spray and akka The API should expose some kind of user CRUD. I'll use only create user in this question...

trait DefaultJsonFormats extends DefaultJsonProtocol with SprayJsonSupport with MetaMarshallers {}

class RegistrationService(registration: ActorRef)
   (implicit executionContext: ExecutionContext) 
                      extends Directives with DefaultJsonFormats {
implicit val timeout = Timeout(2.seconds)
implicit val userFormat = jsonFormat3(User)
implicit val registerFormat = jsonFormat1(Register)
implicit val registeredFormat = jsonFormat1(Registered)

  val route =
    path("register") {
      post {  handleWith { ru: Register => (registration ? ru).mapTo[Registered] } }
}

//------ Actor

object RegistrationActor {
  case class User(id:String, name:String)
  case class Register(user: User)
  case class Registered(status: String)
  case object NotRegistered
}

class RegistrationActor(implDef: String) extends Actor {
 def receive: Receive = {
    case Register(user)=>
        val status=// create user real code with return status
        sender ! new Registered(status)
 } }

In this approach the json serialization and desiarelization is pretty annoying. For every object I need to deal with API I must define the appropriate format

implicit val userFormat = jsonFormat3(User)
implicit val registerFormat = jsonFormat1(Register)
implicit val registeredFormat = jsonFormat1(Registered) 

I would like to avoid such definition and use some general json converter and return a pojo objects, so the conversion will happen under-the-hood

The question is how can I change this code to use by default Gson/Jackson/Spray default converter and avoid definition of the implicit ... jsonFormats?

Upvotes: 3

Views: 723

Answers (1)

Rich
Rich

Reputation: 15464

For every object I need to deal with API I must define the appropriate format

It is normal to do this once, in a "JsonProtocol" class and import that where needed, rather than defining new formats each time:

import MyJsonProtocol._

val route =
  path("register") {
    post {  handleWith { ru: Register => (registration ? ru).mapTo[Registered] } }

how can I change this code to use by default Gson/Jackson/Spray default converter and avoid definition of the implicit ... jsonFormats?

You would need to declare an implicit marshaller from Registered to HttpResponse (or an intermediate value like String) which was backed by Jackson instead of spray-json, then import that marshaller instead of SprayJsonSupport.

Have a look at the implementation of SprayJsonSupport to see how to do this. It's fairly straightforward, if you're comfortable with implicit conversions.

You can also see how this is done in Json4sSupport in Spray -- that trait implements a Marshaller[T, String] for ALL types T. Then, at runtime, the Json4s library will try to serialize the object to JSON.

In this approach the json serialization and desiarelization is pretty annoying

There are two main advantages of spray-jsons approach over Jackson's:

  • There is no reflection, so it is faster at runtime
  • This is no runtime determining of JSON formats, so any issues are caught at compile-time

Upvotes: 3

Related Questions