Как создать пользовательскую кодировку типов Option с помощью Circe? - PullRequest
3 голосов
/ 04 мая 2019

Возможно иметь класс, который выглядит следующим образом:

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
}

Как можно добиться этого мелкозернистого контроля?Я пробовал различные трюки с forProduct2 и mapJsonObject, но не мог заставить его вести себя правильно:

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 Ответ

3 голосов
/ 04 мая 2019

Если ожидаемое поведение {"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] в ближайшей области видимости (объект-спутник данных), он не будет искать неявные кодировщики в глобальных областях. давая вам именно то, что вы хотите.

...