【问题标题】:How do I ignore decoding failures in a JSON array?如何忽略 JSON 数组中的解码失败?
【发布时间】:2018-11-14 05:41:10
【问题描述】:

假设我想将 JSON 数组中的一些值解码为带有circe 的案例类。以下工作正常:

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)))

但有时我正在解码的 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)))

如何编写一个解码器来忽略数组中无法解码到我的案例类中的任何内容?

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

    解决这个问题最直接的方法是使用解码器,它首先尝试将每个值解码为Foo,然后在Foo 解码器失败时回退到标识解码器。 circe 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] 值的结果列表并删除所有Rights”。

    ...它以相当简洁的组合方式完成我们想要的工作。在某些时候,我们可能希望将其捆绑到一个实用程序方法中,但现在写出这个显式版本还不错。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-17
      • 1970-01-01
      • 2022-12-31
      • 1970-01-01
      • 2020-01-18
      • 1970-01-01
      • 2017-05-26
      • 1970-01-01
      相关资源
      最近更新 更多