如何让 circe 为生成的 Json 设置一个 either/or 输出场景?
How do I get circe to have an either/or output scenario for the generated Json?
这就是我的意图 - 假设我有一个名为 medical_payments
的字段 - 它可以 "either" 是 limit 如果选择或 豁免
{
"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
类似,将额外的 Waived:{...}
添加到 Json。
我希望它是 either/or。实现此目标的最佳方法是什么?
我能想到的唯一方法(不是我喜欢的)是使用 forProductN
函数 per the doc 并手动完成所有这些 - 但对于一个大 Json.
您可以几乎使用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"
}
}
(请注意,我还将 Scala 大小写 class 成员名称更改为 Scala 惯用的驼峰式大小写,并在配置中处理到蛇形大小写的转换。)
这不是您想要的,因为有那个额外的 type
成员,但是 circe 的泛型推导只支持可往返的编码器/解码器,并且没有某种鉴别器——要么像这样的成员或您在问题中指出的额外对象层 - 不可能通过 JSON.
来回任意 ADT 的值
这可能没问题——您可能不关心对象中额外的 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 编码器中删除。
这就是我的意图 - 假设我有一个名为 medical_payments
的字段 - 它可以 "either" 是 limit 如果选择或 豁免
{
"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
类似,将额外的 Waived:{...}
添加到 Json。
我希望它是 either/or。实现此目标的最佳方法是什么?
我能想到的唯一方法(不是我喜欢的)是使用 forProductN
函数 per the doc 并手动完成所有这些 - 但对于一个大 Json.
您可以几乎使用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"
}
}
(请注意,我还将 Scala 大小写 class 成员名称更改为 Scala 惯用的驼峰式大小写,并在配置中处理到蛇形大小写的转换。)
这不是您想要的,因为有那个额外的 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 编码器中删除。