如何使生成的Json具有“或”或“或”输出方案?

时间:2019-05-03 00:27:58

标签: scala circe

这就是我想要的-假设我有一个名为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来说很麻烦

1 个答案:

答案 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编码器中删除。