2

我正在编写一个 Circe 解析器,其中架构要求至少设置两个字段之一。这是非常具体的,它似乎不是一种使用 Circe API 轻松完成的方法。

让我们调用我们的字段texthtml.

我已经尝试创建一个类,让我们调用它Content,将它作为单个参数添加到通用模型中,如果它的字段(文本和 html)都为 None,则在构造函数中引发异常。问题是如何定义解码器,因为如果我做这样的事情

implicit val decodeContent: Decoder[ItemContent] =
    Decoder.forProduct2("text", "html")(Content.apply)

无论如何,它都要求两个字段都存在。

我想要的是有一个解码器,如果该字段丢失,则将 None 传递给,Content.apply但我认为这不是预期的行为。

否则应该有一个完全不同的解决方案,但我想不出一个。

谢谢

4

1 回答 1

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)
    }
}

可运行版本

于 2017-08-14T08:32:50.520 回答