这就是我想要的-假设我有一个名为medical_payments
的字段-如果选择当选或放弃 ,它都可以成为 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
类似,在Json中添加了另外的Waived:{...}
。
我希望它可以是“或”。实现此目标的最佳方法是什么?
我唯一想到的方法(不是我喜欢的)是使用forProductN
函数per the doc并手动完成所有这些操作-但这对于大型Json来说很麻烦
答案 0 :(得分:5)
您可以使用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案例类的成员名称更改为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编码器中删除。