如何使用Circe创建选项类型的自定义编码?

时间:2019-05-04 01:48:26

标签: scala circe

可能有一个看起来像这样的类:

case class Amount(value: Int)
case class Data(insurance: Option[Amount], itemPrice: Amount)

如果insurance = None应该获得默认值waived: true

例如:

Data(Some(123),100).asJson

// output
{
  "insurance": {
    "value": 123
  },
  "price": 100
}

And when no Insurance is opted for:

Data(None,100).asJson

// output
{
  "insurance": {
    "waived: true
  },
  "price": 100
}

如何实现这种细粒度的控制?我用forProduct2mapJsonObject尝试了各种技巧,但无法使其正常运行:

implicit val testEncoder = deriveEncoder[Option[Amount]].mapJsonObject(j => {

    val x = j("Some") match {
      case Some(s) => // need to convert to [amount -> "value"]
      case None => JsonObject.apply(("waived",Json.fromBoolean(true)))
    }

    x
  })

这可以很容易地让我了解waived:true部分,但不知道如何处理Some(s)情况。

1 个答案:

答案 0 :(得分:3)

如果对任何{"waived": true}而言,如果预期它们都具有Option[Amount]行为,那么如果您为Option[Amount]编写自定义编码器,则可以依靠半自动派生编码器

这是一个例子

import io.circe.{Encoder, Json}
import io.circe.syntax._
import io.circe.generic.semiauto._

case class Amount(value: Int)
case class Data(insurance: Option[Amount], itemPrice: Amount)

object Amount {
  implicit val encoder: Encoder[Amount] = deriveEncoder
}

object Data {
  implicit val encoderOptionalAmount: Encoder[Option[Amount]] = (optA: Option[Amount]) =>
      optA match {
        case Some(amount) => amount.asJson
        case None => Json.obj("waived" -> true.asJson)
      }

  implicit val encoder: Encoder[Data] = deriveEncoder[Data]
}

println(Data(insurance = None, itemPrice = Amount(10)).asJson)

/*
{
  "insurance" : {
    "waived" : true
  },
  "itemPrice" : {
    "value" : 10
  }
}
*/

工作原理:deriveEncoder[Data]将为itemPrice(类型为Amount)和类型为Option[Amount]的保险调用隐式编码器。

Option[T]的默认编码器只是跳过了None的值,但是由于我们在最近的范围(数据对象伴随)中为Option[T]定义了另一个隐式编码器,因此它看起来不会全局范围内的隐式编码器,可为您提供所需的确切信息。