假设我想将JSON数组中的某些值解码为circe的case类。以下工作正常:
true
有时,我解码的JSON数组包含其他非scala> import io.circe.generic.auto._, io.circe.jawn.decode
import io.circe.generic.auto._
import io.circe.jawn.decode
scala> case class Foo(name: String)
defined class Foo
scala> val goodDoc = """[{ "name": "abc" }, { "name": "xyz" }]"""
goodDoc: String = [{ "name": "abc" }, { "name": "xyz" }]
scala> decode[List[Foo]](goodDoc)
res0: Either[io.circe.Error,List[Foo]] = Right(List(Foo(abc), Foo(xyz)))
形状的东西,这会导致解码错误:
Foo
如何编写一个忽略数组中无法解码到我的case类中的任何内容的解码器?
答案 0 :(得分:3)
解决此问题最直接的方法是使用首先尝试将每个值解码为Foo
的解码器,然后在Foo
解码器失败时返回到身份解码器。第0.9节中的新either
方法使得它的通用版本实际上是单行的:
import io.circe.{ Decoder, Json }
def decodeListTolerantly[A: Decoder]: Decoder[List[A]] =
Decoder.decodeList(Decoder[A].either(Decoder[Json])).map(
_.flatMap(_.left.toOption)
)
它的工作原理如下:
scala> val myTolerantFooDecoder = decodeListTolerantly[Foo]
myTolerantFooDecoder: io.circe.Decoder[List[Foo]] = io.circe.Decoder$$anon$21@2b48626b
scala> decode(badDoc)(myTolerantFooDecoder)
res2: Either[io.circe.Error,List[Foo]] = Right(List(Foo(abc), Foo(xyz)))
分解步骤:
Decoder.decodeList
说“定义一个列表解码器,尝试使用给定的解码器解码每个JSON数组值”。Decoder[A].either(Decoder[Json]
说“首先尝试将值解码为A
,如果失败则将其解码为Json
值(将始终成功),并返回结果(如果有的话)Either[A, Json]
“。.map(_.flatMap(_.left.toOption))
说“获取Either[A, Json]
值的结果列表并删除所有Right
s。”......我们以一种相当简洁,有组合的方式做我们想要的事情。在某些时候,我们可能希望将其捆绑到circe本身的实用程序方法中,但是现在写出这个显式版本并不是太糟糕。