强制执行"应该存在两个字段中的至少一个"在Circe的统治

时间:2017-08-13 16:21:07

标签: json scala circe

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

让我们调用我们的字段texthtml

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

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

无论如何它都需要两个字段。

我希望有一个解码器,如果字段丢失,则将“无”传递给Content.apply,但我不认为这是预期的行为。

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

谢谢

1 个答案:

答案 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")))
}

Runnable version

为避免指定其他字段,可以使用半自动派生作为基础:

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

Runnable version