您可以使用Decoder#emap
:
import io.circe._, parser._
case class ItemContent(text: Option[String], html: Option[String])
object ItemContent {
implicit val decoder =
Decoder.forProduct2("text", "html")(ItemContent.apply).emap {
case ItemContent(None, None) => Left("Neither text nor html is present")
case x => Right(x)
}
}
assert {
decode[ItemContent]("{}").isLeft &&
decode[ItemContent]("""{"html": "foo"}""") == Right(
ItemContent(None, Some("foo"))) &&
decode[ItemContent]("""{"text": "bar"}""") == Right(
ItemContent(Some("bar"), None)) &&
decode[ItemContent]("""{"html": "foo", "text": "bar"}""") == Right(
ItemContent(Some("bar"), Some("foo")))
}
可运行版本
为避免指定其他字段,可以使用半自动派生作为基础:
import io.circe._, parser._, io.circe.generic.semiauto._
case class ItemContent(text: Option[String],
html: Option[String],
other: Int,
fields: String)
object ItemContent {
implicit val decoder =
deriveDecoder[ItemContent].emap { ic =>
if (ic.text.isEmpty && ic.html.isEmpty)
Left("Both `text` and `html` are missing")
else Right(ic)
}
}
可运行版本