Как мне заставить circe иметь сценарий вывода «или/или» для сгенерированного Json?

Вот что я имею в виду — скажем, у меня есть поле с именем medical_payments — оно может «либо» быть ограничением, если кто-то выберет или отменит

{
  "medical_payments": 
    {
      "limit_value":"one_hundred"
    }
} 

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

{
  "medical_payments": 
    {
      "waived":true
    }
} 

Пока вот что у меня есть:

sealed trait LimitOrWaiver
case class Limit(limit_key: String) extends LimitOrWaiver
case class Waived(waived: Boolean) extends LimitOrWaiver

case class Selection(medical_payments: LimitOrWaiver)

Образец данных:

Selection(medical_payments = Limit("one_hundred")).asJson

Выход:

{
  "medical_payments": 
    {
      "Limit": { "limit_value":"one_hundred" } // additional object added
    }
} 

Точно так же для Selection(medical_payments = Waived(true)).asJson в Json добавляется дополнительный Waived:{...}.

Я бы хотел, чтобы это было или/или. Каков наилучший способ добиться этого?

Единственный способ, который я смог придумать (не по моему вкусу), — это использовать forProductN функции согласно документу и делать все это вручную, но это слишком громоздко для большого Json.


person PhD    schedule 03.05.2019    source источник


Ответы (1)


Вы можете почти добиться этого с помощью универсального наследования, используя конфигурацию в generic-extras:

sealed trait LimitOrWaiver
case class Limit(limitValue: String) extends LimitOrWaiver
case class Waived(waived: Boolean) extends LimitOrWaiver
case class Selection(medicalPayments: LimitOrWaiver)

import io.circe.generic.extras.Configuration, io.circe.generic.extras.auto._
import io.circe.syntax._

implicit val codecConfiguration: Configuration =
  Configuration.default.withDiscriminator("type").withSnakeCaseMemberNames

А потом:

scala> Selection(medicalPayments = Limit("one_hundred")).asJson
res0: io.circe.Json =
{
  "medical_payments" : {
    "limit_value" : "one_hundred",
    "type" : "Limit"
  }
}

(Обратите внимание, что я также изменил имена членов класса case в Scala на Scala-идиоматический Camel-Case, и я обрабатываю преобразование в Snake-Case в конфигурации.)

Это не совсем то, что вам нужно, так как есть этот дополнительный элемент type, но общий вывод circe поддерживает только кодеры/декодеры с двусторонним обходом и без какого-либо дискриминатора — либо такой член, либо дополнительный объектный слой, который вы указать в вопросе - невозможно передать значения произвольных АТД через JSON.

Это может быть хорошо — вы можете не заботиться о дополнительных type в вашем объекте. Если вам не все равно, вы все равно можете использовать вывод с небольшой дополнительной работой:

import io.circe.generic.extras.Configuration, io.circe.generic.extras.auto._
import io.circe.generic.extras.semiauto._
import io.circe.ObjectEncoder, io.circe.syntax._

implicit val codecConfiguration: Configuration =
  Configuration.default.withDiscriminator("type").withSnakeCaseMemberNames

implicit val encodeLimitOrWaiver: ObjectEncoder[LimitOrWaiver] =
  deriveEncoder[LimitOrWaiver].mapJsonObject(_.remove("type"))

А также:

scala> Selection(medicalPayments = Limit("one_hundred")).asJson
res0: io.circe.Json =
{
  "medical_payments" : {
    "limit_value" : "one_hundred"
  }
}

Если бы вы действительно хотели, вы могли бы даже сделать это автоматически, чтобы type было удалено из любых кодировщиков ADT, которые вы получаете.

person Travis Brown    schedule 03.05.2019
comment
Это действительно полезно. Мне придется удалить type AFAIK. И по какой-то причине withSnakeCaseMember... не изменяет вложенные объекты. Так что нужно продолжать поставлять deriveEncoders для всего. С тем же успехом можно удалить type, как вы предложили. Вздох. Иногда мне кажется, что Цирцея вызывает больше головной боли, чем что-либо еще :) - person PhD; 03.05.2019
comment
@PhD Вам не нужно использовать общий вывод! - person Travis Brown; 03.05.2019
comment
ВЫ автор Circe?!? (Только что увидел ваше имя в нескольких файлах кода :) - person PhD; 03.05.2019
comment
Ты будешь ненавидеть меня за это. Еще один: stackoverflow.com/questions/55976376/: D - person PhD; 03.05.2019