在 Circe 执行 "at least one of two fields should be present" 规则
Enforce a "at least one of two fields should be present" rule in Circe
我正在编写一个 Circe 解析器,其中的架构要求至少设置两个字段之一。这是非常具体的,它似乎不是用 Circe API 轻松完成的方法。
让我们称我们的字段为 text
和 html
。
我已经尝试创建一个 class,我们称它为 Content
,将它作为单个参数添加到通用模型中,如果它的两个字段(文本和html) 是 None。问题是如何定义解码器,因为如果我做这样的事情
implicit val decodeContent: Decoder[ItemContent] =
Decoder.forProduct2("text", "html")(Content.apply)
它要求两个字段都存在。
我想要一个解码器,如果缺少该字段,则将 None 传递给 Content.apply
,但我认为这不是预期的行为。
否则应该有一个完全不同的解决方案,但我想不出一个。
谢谢
您可以使用 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)
}
}
我正在编写一个 Circe 解析器,其中的架构要求至少设置两个字段之一。这是非常具体的,它似乎不是用 Circe API 轻松完成的方法。
让我们称我们的字段为 text
和 html
。
我已经尝试创建一个 class,我们称它为 Content
,将它作为单个参数添加到通用模型中,如果它的两个字段(文本和html) 是 None。问题是如何定义解码器,因为如果我做这样的事情
implicit val decodeContent: Decoder[ItemContent] =
Decoder.forProduct2("text", "html")(Content.apply)
它要求两个字段都存在。
我想要一个解码器,如果缺少该字段,则将 None 传递给 Content.apply
,但我认为这不是预期的行为。
否则应该有一个完全不同的解决方案,但我想不出一个。
谢谢
您可以使用 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)
}
}