如何忽略JSON数组中的解码失败? [英] How do I ignore decoding failures in a JSON array?

查看:77
本文介绍了如何忽略JSON数组中的解码失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想使用 circe 将JSON数组中的某些值解码为case类.以下工作正常:

Suppose I want to decode some values from a JSON array into a case class with circe. The following works just fine:

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形的东西,这会导致解码错误:

It's sometimes the case that the JSON array I'm decoding contains other, non-Foo-shaped stuff, though, which results in a decoding error:

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

我该如何编写一个解码器来忽略数组中无法解码为我的case类的所有内容?

How can I write a decoder that ignores anything in the array that can't be decoded into my case class?

推荐答案

解决此问题的最直接方法是使用解码器,该解码器首先尝试将每个值解码为Foo,然后返回到恒等式Foo解码器失败时解码器.大约在0.9版中的新either方法使该通用版本实际上变成了单行代码:

The most straightforward way to solve this problem is to use a decoder that first tries to decode each value as a Foo, and then falls back to the identity decoder if the Foo decoder fails. The new either method in circe 0.9 makes the generic version of this practically a one-liner:

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".
  • Decoder.decodeList says "define a list decoder that tries to use the given decoder to decode each JSON array value".
  • Decoder[A].either(Decoder[Json] says "first try to decode the value as an A, and if that fails decode it as a Json value (which will always succeed), and return the result (if any) as a Either[A, Json]".
  • .map(_.flatMap(_.left.toOption)) says "take the resulting list of Either[A, Json] values and remove all the Rights".

…这以一种简洁,结构化的方式完成了我们想要的工作.在某些时候,我们可能希望将其捆绑成一个实用工具,但现在写出这个显式版本还不错.

…which does what we want in a fairly concise, compositional way. At some point we might want to bundle this up into a utility method in circe itself, but for now writing out this explicit version isn't too bad.

这篇关于如何忽略JSON数组中的解码失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆