Как добавить пользовательский маршаллер в 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]? Если да, просто исправьте это...

Если нет, вы должны следовать документу и создавать:

  1. как модель домена, конкретный тип результата, чтобы удерживать ответ, например, case class CurrencyResult(currency: Currency, code: String)
  2. в trait JsonSupport: JsonFormat[Currency]
  3. в 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

}