假设我想使用circe将JSON数组中的某些值解码为case类。以下工作正常:
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)))
Run Code Online (Sandbox Code Playgroud)
但是,有时候我正在解码的JSON数组包含其他非Foo形的东西,这会导致解码错误:
scala> val badDoc =
| """[{ "name": "abc" }, { "id": 1 }, true, "garbage", { "name": "xyz" }]"""
badDoc: String = [{ "name": "abc" }, { "id": 1 }, true, "garbage", { "name": "xyz" }]
scala> decode[List[Foo]](badDoc)
res1: Either[io.circe.Error,List[Foo]] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(name), MoveRight, DownArray)))
Run Code Online (Sandbox Code Playgroud)
如何编写一个忽略数组中无法解码为我的case类的内容的解码器?
解决这个问题最直接的方法是使用一个解码器,它首先尝试将每个值解码为 a Foo,然后如果Foo解码器失败则回退到身份解码器。eithercirce 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)
)
Run Code Online (Sandbox Code Playgroud)
它是这样工作的:
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)))
Run Code Online (Sandbox Code Playgroud)
分解步骤:
Decoder.decodeList 说“定义一个列表解码器,尝试使用给定的解码器来解码每个 JSON 数组值”。Decoder[A].either(Decoder[Json]说“首先尝试将值解码为A,如果失败,则将其解码为Json值(总是会成功),并将结果(如果有)返回为Either[A, Json]“。.map(_.flatMap(_.left.toOption))说“获取结果Either[A, Json]值列表并删除所有Rights”。......它以一种相当简洁、组合的方式完成了我们想要的。在某些时候,我们可能希望将其捆绑到 circe 本身的实用程序方法中,但现在写出这个显式版本还不错。
| 归档时间: |
|
| 查看次数: |
528 次 |
| 最近记录: |