Как добавить кастомный маршаллер в 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, но даже чтение документации я очень не уверен, как действовать дальше. Описано несколько подходов, и я не уверен, какой из них подходит мне и с чего начать.


Я пробовал создать свой 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. Это полностью нарушило компиляцию проекта.

Для преобразования json должна быть доступна некоторая информация о типе. Однако использование HashMap приводит к потере информации об этом типе. Вот почему я предложил вам использовать в своем ответе case class, что позволит вам не потерять необходимую информацию о типе за счет записи еще одного JsonFormat.

Frederic A. 11.04.2018 14:11

Только что обновил свой ответ

Frederic A. 11.04.2018 14:18

@FredericA У меня есть множество объектов ответа, все являются классами case, и многие из них имеют объекты валюты на разных уровнях вложенности, а также в последовательностях. Хеш-карта приведена только в качестве примера, чтобы продемонстрировать проблему. Я хочу, чтобы объекты валюты конвертировались в любой класс, независимо от того, где он появляется. Currenlty, я получаю только {}. Насколько я понимаю, мне кажется, что мне нужно создать маршаллер для каждого класса кейсов, который кажется избыточным и многословным.

k0pernikus 12.04.2018 11:03

Да, вы должны создать JsonFormat для каждого класса кейса. Но все же вам нужно написать JsonFormat только один раз для таких членов, как Currency, они будут найдены с помощью неявного разрешения. Запись каждого JsonFormat по отдельности считается нормальным явлением, поскольку они представляют собой открытый интерфейс вашего модуля / приложения.

Frederic A. 12.04.2018 12:31

Тем не менее, я до сих пор не понимаю, почему ваш Currency форматируется как {}. У вас уже есть JsonFormat для этого?

Frederic A. 12.04.2018 12:36

@FredericA. Нет, не думаю, что у меня для этого есть JsonFormat. Зачем мне это нужно / нужно? Весь процесс сортировки по-прежнему кажется мне черной магией, и я не понимаю, почему он стоит и как его настроить.

k0pernikus 12.04.2018 14:27
13
6
1 858
3

Ответы 3

У вас есть JsonFormat[Currency]? Если да, просто исправь ...

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

  1. как модель предметной области, конкретный тип результата для хранения ответа, например case class CurrencyResult(currency: Currency, code: String)
  2. в trait JsonSupport: JsonFormat[Currency]
  3. в trait JsonSupport: JsonFormat[CurrencyResult]

В качестве альтернативы пункту 1 выше вы, вероятно, можете ввести свою карту как: Map[String, JsValue].

Мне это не помогает, поскольку предполагает наличие некоторых знаний, которых у меня нет. Не могли бы вы пояснить? Кроме того, я хочу воздействовать на любой объект Currency в любом классе Case, независимо от того, где он появляется в ответе.

k0pernikus 11.04.2018 13:49

Я пользуюсь поддержкой 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

}

См. Мой обновленный вопрос, поскольку я пытался использовать собственный JsonProtocol, но он ломает мой проект, и я не знаю почему.

k0pernikus 11.04.2018 13:52

это потому, что ваш сериализатор не входит в область видимости. Просто импортируйте его, и он должен скомпилироваться. С помощью Spray вы должны расширить SprayJsonSupport. Обратите внимание, что вы должны добавить библиотеку зависимостейDependencies + = "com.typesafe.akka" %% "akka-http-spray-json"% "10.1.1"

EmiCareOfCell44 11.04.2018 15:03

Я не совсем уверен, что в этом проблема. Вы видели мой обновленный вопрос?

k0pernikus 11.04.2018 15:56

Насколько я понимаю, у вас есть все части, вам просто нужно их сложить.

Spray json обеспечивает поддержку маршалинга общих типов, таких как Int, String, Boolean, List, Map и т. Д. Но он не знает, как маршалировать «Валюту». И чтобы решить эту проблему, вы создали настраиваемый маршаллер для объектов «Валюта». Вам просто нужно подключить его в нужном месте. Все, что вам нужно сделать, это импортировать маршаллер с вашего CurrencyJsonProtocol в ваш контроллер, как показано ниже:

import CurrencyJsonProtocol._

Также убедитесь, что у вас есть следующие импортированные файлы:

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

И spray-json должен это автоматически подобрать.

Чтобы понять, как это работает, вам нужно разобраться в подразумевает в scala. Хотя это похоже на волшебство, когда вы пришли из мира Java, как я, я могу вас заверить, что это не так.

Другие вопросы по теме

Похожие вопросы

Почему в javase нет поддержки json?
При подписании jwt, созданного с помощью json учетной записи службы gcp для iap, возникает ошибка: «в знаке () отсутствует 1 обязательный позиционный аргумент:« алгоритм »
Как я могу использовать патч-документ json с помощью vb.net
Scala: использование подстановочного знака с option в сопоставлении с образцом для класса case со слишком большим количеством параметров
Вставка файла json в браузере не работает - getasfile возвращает null
Какие параметры мне нужны в объектном классе для де-сериализации нижеприведенных данных, поступающих из вызова api?
Как сделать так, чтобы функция kafka-spark kafkautils.createdirectstream потребляла из локального кеша реестра вместо отправки запроса в реестр схемы
Заполнение mongodb nodejs в скрипте сидера
Невозможно получить токен ответа json для входа в параметры профиля json в быстром режиме
Как я могу добавить дополнительное поле при маршалинге сообщения go protobuf в json?