Как я могу настроить Spring для использования Json4s для сериализации тел ответов?

Я использую Spring Boot в проекте Scala, и я уже использую Json4 для сериализации и десериализации JSON. До сих пор я писал такие конечные точки:

@RequestMapping(path = Array("/getSomething"), produces = Array(MediaType.APPLICATION_JSON_VALUE))
def getSomething: String = {
  // do some things
  val resultValue: ResultType = ??? // where ResultType is some case class that can be serialized with json4s
  json4s.native.Serialization.write(resultValue)
}

Однако мне бы очень хотелось избежать этого последнего шага, сделав при этом более понятным, какой тип возврата конечной точки. Кроме того, я хотел бы иметь возможность извлекать возвращаемый тип для создания документов API. Вместо этого я хотел бы написать то же самое, например:

@RequestMapping(path = Array("/getSomething"), produces = Array(MediaType.APPLICATION_JSON_VALUE))
def getSomething: ResultType = {
  // do some things
  resultValue
}

Однако когда я это делаю, результат всегда будет просто {}. Я предполагаю, что это связано с тем, что Spring используется Spring вместо Json4s, и у меня нет классов case, аннотированных для использования с Jackson. Все, что я хочу сделать, это добавить перехватчик, который вызывается на каждой конечной точке и преобразует результат в строку JSON. Написать перехватчик легко (это было бы просто json4s.native.Serialization.write), но как я могу зарегистрировать его, чтобы Spring каждый раз автоматически использовал его?


person Ian    schedule 21.10.2020    source источник
comment
ВНИМАНИЕ: Json4s уязвим. под DoS / DoW-атаками!   -  person Andriy Plokhotnyuk    schedule 22.10.2020


Ответы (1)


Вы можете использовать модуль json4s-jackson и зарегистрировать свой класс case с помощью настраиваемого сериализатора, создав Spring Bean, например:

  @Bean def json4sCustomizer: Jackson2ObjectMapperBuilderCustomizer = builder => {
    builder.serializerByType(classOf[ResultType], new JsonSerializer[ResultType] {
      val json4sSerializer = new json4s.jackson.JValueSerializer
      implicit val formats:Formats = DefaultFormats

      override def serialize(value: ResultType, gen: JsonGenerator, serializers: SerializerProvider): Unit =
        json4sSerializer.serialize(json4s.Extraction.decompose(value), gen, serializers)
    })
  }

Для полной замены Джексона потребуется Конвертеры HTTP-сообщений и может быть более / менее удобным, в зависимости от других задач вашего приложения.

  @Bean def json4sConverter: HttpMessageConverter[AnyRef] = new AbstractJsonHttpMessageConverter {
    override def readInternal(resolvedType: Type, reader: Reader): AnyRef = ???
    override def writeInternal(value: Any, typ: Type, writer: Writer): Unit = ???
  }
person Ryan Scheidter    schedule 21.10.2020
comment
Спасибо за информацию. Не могли бы вы подробнее рассказать об использовании конвертеров сообщений? Я вижу AbstractJsonHttpMessageConverter, но не уверен, как создать и зарегистрировать его таким образом, чтобы он использовался для каждого типа возвращаемого значения (AnyRef). Мне кажется, что способ конвертера сообщений более применим к моему варианту использования, поскольку я хочу использовать его для каждой конечной точки. - person Ian; 22.10.2020
comment
Подход сериализатора будет работать для каждой конечной точки, использующей зарегистрированный тип. Если у вас несколько классов case, вы можете зарегистрировать их с помощью общего признака, даже scala.Product - person Ryan Scheidter; 22.10.2020