如何忽略JSON数组中的解码失败?

时间:2018-06-04 20:19:06

标签: json scala circe

假设我想将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类中的任何内容的解码器?

1 个答案:

答案 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本身的实用程序方法中,但是现在写出这个显式版本并不是太糟糕。