Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a custom marshaller to akka http?

As a beginner to both scala and akka-http, I am trying to hook into the serialization aka marshalling process.

The project uses [email protected] and [email protected]". Furthermore, it has the akka-http-spray-json dependency included.

In the codebase, we use Java.Util.Currency (It may be deprecated which is not important as I'd still like to know how to add a custom marshaller.)

Given this example controller:

def getCurrencyExample: Route = {
    path("currencyExample") {
      val currency: Currency = Currency.getInstance("EUR")
      val code: String = currency.getCurrencyCode

      val shouldBeFormated = collection.immutable.HashMap(
        "currencyCode" -> code,
        "currencyObject" -> currency
      )

      complete(shouldBeFormated)
    }
  }

I get a response like this where the currency object becomes empty:

 {
  currencyObject: { },
  currencyCode: "EUR",
 }

I expect something like:

 {
  currencyObject: "EUR",
  currencyCode: "EUR",
 }

The currency object should be transformed into a JSON string. And since I do not want to transform each response manually, I want to hook into marshalling process and have it done in the background.

I want to add a custom marhaller only for Java.Util.Currency objects, yet even reading up on the docs I am very unsure how to proceed. There are multiple approaches described, and I am not sure which fits my need, or where to start.


I tried creating my own CurrencyJsonProtocol:

package com.foo.api.marshallers

import java.util.Currency

import spray.json.{DefaultJsonProtocol, JsString, JsValue, RootJsonFormat}

object CurrencyJsonProtocol extends DefaultJsonProtocol {

  implicit object CurrencyJsonFormat extends RootJsonFormat[Currency] {
    override def read(json: JsValue): Currency = {
      Currency.getInstance(json.toString)
    }

    override def write(obj: Currency): JsValue = {
      JsString(obj.getCurrencyCode)
    }
  }

}

yet the mere existence of the file breaks my project:

[error] RouteDefinitons.scala:90:16: type mismatch;
[error]  found   : scala.collection.immutable.HashMap[String,java.io.Serializable]
[error]  required: akka.http.scaladsl.marshalling.ToResponseMarshallable
[error]       complete(shouldBeFormated)
[error]                ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed

and I have no idea why. (It was crashing due to my package name being called marshaller. That completely broke the compilation of the project.

like image 322
k0pernikus Avatar asked Apr 11 '18 09:04

k0pernikus


People also ask

Is marshalling the same as serialization?

Marshalling is like serialization, except marshalling also records codebases. Marshalling is different from serialization in that marshalling treats remote objects specially. Any object whose methods can be invoked [on an object in another Java virtual machine] must implement the java.

What is Unmarshall?

“Unmarshalling” is the process of converting some kind of a lower-level representation, often a “wire format”, into a higher-level (object) structure. Other popular names for it are “Deserialization” or “Unpickling”.

What is marshalling in Scala?

Marshalling is the process of converting a higher-level (object) structure into some kind of lower-level representation, often a “wire format”. Other popular names for marshalling are “serialization” or “pickling”.


1 Answers

From what I understand, you have all the pieces, you just need to put them together.

Spray json provides support for marshalling common types, such as Int, String, Boolean, List, Map, etc. But it doesn't know how to marshall 'Currency'. And to solve that you have created a custom marshaller for 'Currency' objects. You just need to plug it in the right place. All that you have to do is import the marshaller from your CurrencyJsonProtocol into your Controller like below:

import CurrencyJsonProtocol._

Also make sure you have the below imports as well:

import spray.httpx.SprayJsonSupport._ import spray.json.DefaultJsonProtocol._

And spray-json should automatically pick that up.

To understand how it works, you need to understand about implicits in scala. Although it looks like magic when you come from java-world like me, I can assure you it's not.

like image 80
James Avatar answered Oct 16 '22 16:10

James