Как сериализовать вложенный (общий) класс case

#scala #json4s

#scala #json4s

Вопрос:

Ищу некоторую помощь по сериализации вложенного объекта case с использованием json4s. Проблема в том, что объект case определяется во время выполнения, поэтому, похоже, мне понадобится доступ к внешним форматерам? например

 trait Base { val id: Long }
case class ChildA(id: Long, name:String) extends Base
case class ChildB(id: Long, name:String) extends Base
case class WithTimestamp[E <: Base](base:E, timestamp:Long)
 

теперь, если я сериализуюwithtimestamp, я получаю, например

 {"base":{"id":1, "name":"foo"}, timestamp:12345 }
 

но то, что я хотел бы получить, это:

 {"id":1, "name":"foo", timestamp:12345 }
 

для чего, по-видимому, требуется пользовательский сериализатор:

 class WithTimestampSerializer[E <: Base] extends CustomSerializer[WithTimestamp[E]](
  f => ( {
    case x: JObject => ... 

  }, {
    case x: WithTimestamp[E] =>
    val j: JValue = write(x.base) // <--- not sure what to do at this point,
                                  // i.e. write either the ChildA or ChildB default serialization
      j merge JObject("timestamp" -> JLong(x.timestamp))
  }))
 

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

Комментарии:

1. Я думаю, вы хотите использовать Extraction.decompose(x.base). И вы, вероятно, можете пропустить дисперсию и заменить WithTimestamp[E] на WithTimestamp[_] . Я бы также рассмотрел возможность изменения / создания модели, используемой для обслуживания данных. В конечном итоге может быть проще, чем пользовательские форматеры.

2. Спасибо, это полезно, но это по-прежнему указывает на то, что мне требуется неявный форматировщик, который, похоже, просто отодвигает проблему на 1 уровень. Добавление неявного средства форматирования дает мне переполнение стека, например. создание какой-то циклической ссылки.

3. О, вам нужно что-то вроде implicit val formats = org.json4s.DefaultFormats new WithTimestampSerializer visible для вашего сериализатора. Или вы могли бы просто перейти f к decompose(…)(f) вместо использования implicits .

4. Это помогло, спасибо

5. @TomerShetah Я рассмотрел это и добавил ответ 🙂

Ответ №1:

Сериализатор будет выглядеть следующим образом:

 class WithTimestampSerializer extends CustomSerializer[WithTimestamp[_]](
  f => ( {
    case x: JObject => ???
  }, {
    case x: WithTimestamp[_] =>
      val j: JValue = Extraction.decompose(x.base)(f) // <- the important part
      j merge JObject("timestamp" -> JLong(x.timestamp))
  }))
 

Вы можете либо использовать Extraction.decompose(x.base)(f) , либо иметь неявный тип Format поблизости.

Однако, если вы захотите десериализовать его или расширить проект, я бы рекомендовал добавить новый класс case только для целей моделирования данных JSON. Это добавляет некоторое повторение, но может быть проще в обслуживании.