解决这个问题最直接的方法是使用一个解码器,该解码器首先尝试将每个值解码为
Foo
,然后返回到身份解码器,如果
福
解码器失败。新的
either
Circe 0.9中的方法使其泛型版本实际上是一行:
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))
说“把结果列表
或者[a,json]
值并删除所有
Right
S”。
它以一种相当简洁的作曲方式完成了我们想要的。在某种程度上,我们可能希望将其捆绑到circe本身的实用程序方法中,但目前写出这个显式版本还不算太糟。