Как добавить пользовательский маршаллер в akka http?
Являясь новичком как scala, так и akka-http, я пытаюсь подключиться к процессу сериализации aka marshalling.
В проекте используются [email protected] и [email protected] ". Кроме того, в него включена зависимость 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. И так как я не хочу преобразовывать каждый ответ вручную, я хочу подключиться к процессу сортировки и сделать это в фоновом режиме.
Я хочу добавить настраиваемый marhaller только для объектов Java.Util.Currency
, но даже прочитав документы, я очень не уверен, как это сделать. Существует несколько подходов, и я не уверен, что подходит мне, или с чего начать.
Я попытался создать свой собственный 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
. Это полностью нарушило компиляцию проекта.
Ответы
Ответ 1
Насколько я понимаю, у вас есть все части, вам просто нужно собрать их вместе.
Spray json предоставляет поддержку для сортировки обычных типов, таких как Int, String, Boolean, List, Map и т.д. Но он не знает, как настроить маркер "Валюта". И чтобы решить, что вы создали настраиваемый маршаллер для объектов "Валюта". Вам просто нужно подключить его в нужном месте. Все, что вам нужно сделать, это импортировать маршаллера из вашего CurrencyJsonProtocol
в ваш контроллер, как показано ниже:
import CurrencyJsonProtocol._
Также убедитесь, что у вас есть и ниже импорт:
import spray.httpx.SprayJsonSupport._ import spray.json.DefaultJsonProtocol._
И распылитель должен автоматически выбрать это.
Чтобы понять, как это работает, вам нужно понять о implicits в scala. Хотя это выглядит как магия, когда вы родом из java-мира, подобного мне, я могу вас заверить.
Ответ 2
У вас есть JsonFormat[Currency]
? Если да, просто исправьте это...
Если нет, вы должны следовать документу и создавать:
- как модель домена, конкретный тип результата, чтобы удерживать ответ, например,
case class CurrencyResult(currency: Currency, code: String)
- в
trait JsonSupport
: JsonFormat[Currency]
- в
trait JsonSupport
: JsonFormat[CurrencyResult]
В качестве альтернативы пункту 1 выше вы можете, например, ввести свою карту как: Map[String, JsValue]
.
Ответ 3
Я использую Spray поддержку в течение многих лет. Может быть, я должен попробовать другой. Во всяком случае, в Spray пользовательский форматтер для типа прост. Пример squants.market.currency https://github.com/typelevel/squant
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
}