[ "Foo", "McBar", true, false, false, false, true, 137 ]
我不知道为什么有人会选择像这样编码他们的数据,但是人们做出了奇怪的事情,而在这种情况下,我只能处理它。
我想将此JSON解码为如下的案例类:
case class Foo(firstName: String, lastName: String, age: Int, stuff: List[Boolean])
我们可以这样写:
import cats.syntax.either._
import io.circe.{ Decoder, DecodingFailure, Json }
implicit val fooDecoder: Decoder[Foo] = Decoder.instance { c =>
c.focus.flatMap(_.asArray) match {
case Some(fnJ +: lnJ +: rest) =>
rest.reverse match {
case ageJ +: stuffJ =>
for {
fn <- fnJ.as[String]
ln <- lnJ.as[String]
age <- ageJ.as[Int]
stuff <- Json.fromValues(stuffJ.reverse).as[List[Boolean]]
} yield Foo(fn, ln, age, stuff)
case _ => Left(DecodingFailure("Foo", c.history))
}
case None => Left(DecodingFailure("Foo", c.history))
}
}
...这是有效的:
scala> fooDecoder.decodeJson(json"""[ "Foo", "McBar", true, false, 137 ]""")
res3: io.circe.Decoder.Result[Foo] = Right(Foo(Foo,McBar,137,List(true, false)))
但是,那太可怕了。而且错误信息完全没有用:
scala> fooDecoder.decodeJson(json"""[ "Foo", "McBar", true, false ]""")
res4: io.circe.Decoder.Result[Foo] = Left(DecodingFailure(Int, List()))
肯定有一种方法可以做到这一点,而不涉及在光标和Json
值之间来回切换,丢弃错误消息中的历史记录,并且通常会让人感到难看?
一些背景:关于编写类似 circe 中自定义 JSON 数组解码器的问题经常出现(例如,今天早上)。如何实现这个的具体细节可能会在 circe 的即将发布的版本中发生改变(尽管 API 将是相似的;请参见 这个实验性项目 以获取一些详细信息),因此我不想花费太多时间将这样的示例添加到文档中,但它出现的频率足够高,我认为它确实值得在 Stack Overflow 上有一个问答。