如何为akka http添加自定义marshaller?

时间:2018-04-11 09:42:26

标签: json scala marshalling spray akka-http

作为scala和akka-http的初学者,我试图加入序列化和编组过程。

该项目使用akka@2.5.2和akka-http@10.0.10“。此外,它还包含akka-http-spray-json依赖项。

在代码库中,我们使用Java.Util.Currency(可能会弃用它并不重要,因为我仍然想知道如何添加自定义编组程序。)

鉴于此示例控制器:

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)
    }
  }

我得到这样的回复,货币对象变空了:

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

我期待的是:

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

currency对象应该转换为JSON字符串。由于我不想手动转换每个响应,我想挂钩编组过程并在后台完成。

我想仅为Java.Util.Currency个对象添加自定义marhaller,但即使reading up on the docs我也不确定如何继续。  描述了多种方法,我不确定哪种方法适合我的需要,或者从哪里开始。

我尝试创建自己的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)
    }
  }

}

但仅仅存在该文件会破坏我的项目:

[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

我不明白为什么。 (由于我的包名称被称为marshaller,它崩溃了。这完全破坏了项目的编译。

3 个答案:

答案 0 :(得分:1)

根据我的理解,你拥有所有的部分,你只需将它们放在一起。

Spray json为编组常用类型提供支持,例如Int,String,Boolean,List,Map等。但是它不知道如何编组'Currency'。并解决您已为“货币”对象创建自定义编组器。你只需要把它插在正确的地方。您需要做的就是将CurrencyJsonProtocol中的编组器导入到控制器中,如下所示:

import CurrencyJsonProtocol._

同时确保您也拥有以下导入:

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

而喷射json应该会自动选择它。

要了解其工作原理,您需要了解scala中的implicits。虽然当你来自像我这样的java世界时它看起来很神奇,但我可以向你保证它不是。

答案 1 :(得分:0)

你有JsonFormat[Currency]吗?如果是,请修理它......

如果没有,您应该按照doc并创建:

  1. 作为域模型,用于保存答案的特定结果类型,例如case class CurrencyResult(currency: Currency, code: String)
  2. 位于trait JsonSupportJsonFormat[Currency]
  3. 位于trait JsonSupportJsonFormat[CurrencyResult]
  4. 作为上述第1点的替代方法,您可以将地图输入为:Map[String, JsValue]

答案 2 :(得分:0)

我多年来一直使用Spray支持。也许我应该尝试另一个。无论如何,在Spray中,一种类型的自定义格式化器很简单。 squants.market.currency https://github.com/typelevel/squants

的一个例子
import squants.market.{ Currency, Money }
import squants.market._

 trait MoneyJsonProtocol extends DefaultJsonProtocol {

  implicit object CurJsonFormat extends JsonFormat[Currency] {
    def write(currency: Currency): JsValue = JsString(currency.code)
    def read(value: JsValue): Currency = value match {
      case JsString(currency) =>
        defaultCurrencyMap.get(currency) match {
          case Some(c) => c
          case None => // throw an exception, for example
        }
      case _ => // throw another exception, for example
    }
  }

  implicit def toJavaBigDecimal(b: math.BigDecimal): java.math.BigDecimal = b.underlying

}