我正在编写一个Circe解析器,其中架构要求设置两个字段中的至少一个。这是非常具体的,它似乎不是一种使用Circe API轻松实现的方法。
让我们调用我们的字段text
和html
。
我已经尝试创建一个类,让它调用它Content
,将它作为单个参数添加到通用模型中,并在构造函数中引发异常,如果它们的两个字段(text和html)没有。问题是如何定义解码器,因为如果我做这样的事情
implicit val decodeContent: Decoder[ItemContent] =
Decoder.forProduct2("text", "html")(Content.apply)
无论如何它都需要两个字段。
我希望有一个解码器,如果字段丢失,则将“无”传递给Content.apply
,但我不认为这是预期的行为。
否则应该有一个完全不同的解决方案,但我想不出一个。
谢谢
答案 0 :(得分:1)
您可以使用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)
}
}