json - Circe 列表反序列化与最佳尝试和错误报告
问题描述
我正在使用 Circe 反序列化包含列表的 json。有时 json 列表中的一些项目已损坏,这会导致整个反序列化失败。相反,我希望 Circe 做出最佳尝试,并返回所有成功反序列化的列表项的列表,以及损坏项的错误列表。这在 Circe 是如何做到最好的?
具体来说,假设我正在尝试反序列化:
val json = """{ "params": {
"playlist": {
"name": "Sample Playlist",
"items": [
{
"clipId":"xyz",
"name":"abc",
"properties": {
"cat": "siamese",
"dog": "spaniel"
}
},
{
"clipId":"pqr",
"name":"def",
"properties": {
"cat": "tabby",
"dog": "terrier"
}
}
]
}
}}"""
我正在这样做:
import io.circe.Decoder, io.circe.generic.auto._
import scala.util._
case class Clip(clipId: String, name: String, dog: String)
implicit val decodeClip: Decoder[Clip] = Decoder.instance { c =>
for {
id <- c.get[String]("clipId")
name <- c.get[String]("name")
dog <- c.downField("properties").get[String]("dog")
} yield {
Clip(id, name, dog)
}
}
val decodeClipsParam = Decoder[List[Clip]].prepare(
_.downField("params").downField("playlist").downField("items")
)
def deserializedThing(theJson: String) = io.circe.parser.decode(theJson)(decodeClipsParam)
它工作正常,并正确反序列化:
scala> deserializedThing(json)
res1: Either[io.circe.Error,List[circeLab.circeLab.Clip]] = Right(List(Clip(xyz,abc,spaniel), Clip(pqr,def,terrier)))
但是,如果我现在损坏了 json 列表中的一个项目(通过更改其中一个"dog"
键来"doggg"
表示),那么整个反序列化将失败 - 它没有给我未损坏Clip
项目的列表,它只是告诉我它失败了。
因此,List[Clip]
我不想反序列化 into ,而是反序列化 into List[Try[Clip]]
,其中每个项目要么是 like Success(Clip(xyz,abc,spaniel))
,要么是Failure(ErrorDescriptionForThatItem)
.
我能够在 Argonaut 中实现这一点(使用一些相当难看的代码),但无法弄清楚 Circe 中的语法。实现这一目标的最佳方法是什么?谢谢!
解决方案
好的,所以这个解决方案有效:
import io.circe.{Json, Decoder}
import io.circe.parser.parse
import scala.util.{Try, Success, Failure}
// Throw this exception if the list of items can't even be retrieved
case class ParseException(msg: String) extends Exception(msg)
case class Clip(clipId: String, name: String, dog: String)
// This is the decoder that tries to decode an individual item
implicit val decodeClip: Decoder[Clip] = Decoder.instance { c =>
for {
id <- c.get[String]("clipId")
name <- c.get[String]("name")
dog <- c.downField("properties").get[String]("dog")
} yield {
Clip(id, name, dog)
}
}
// Turn a string into a json doc
def jsonDoc(str: String) = parse(str).getOrElse(Json.Null)
// Attempt to retrieve the list of json objects appearing in "items"
def getListOfItemsAsJsonObjects(doc: Json): Try[List[Json]] = doc.hcursor.downField("params").downField("playlist").downField("items").focus match {
case None => Failure(ParseException("Couldn't get items"))
case Some(obj) => obj.asArray match {
case None => Failure(ParseException("Couldn't turn to array"))
case Some(arr) => Success(arr.toList)
}
}
// Finally, map each json object from Items to a Try[Clip], so individual corrupted items don't affect others.
def tryListOfTries(str: String) = getListOfItemsAsJsonObjects(jsonDoc(str)).map(ok => ok.map(_.as[Clip].toTry))
如果 json 字符串中没有损坏,tryListOfTries(json)
则将返回:
Success(List(Success(Clip(xyz,abc,spaniel)), Success(Clip(pqr,def,terrier))))
如果你破坏了一个人item
,你会得到这样的东西,而另一个items
解码正常:
Success(List(Failure(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(dog), DownField(properties)))), Success(Clip(pqr,def,terrier))))
而且,如果您在更高级别损坏了某些东西,以至于它甚至无法检索items
数组,那么您将Failure
在顶层获得 a:
Failure(ParseException: Couldn't get items)
不确定是否有更惯用的解决方案,但我找不到一个,所以我希望这对某人有所帮助。
推荐阅读
- angular - 如何覆盖 Angular 中以前的路由,使其不会出现在单击后退按钮时?
- c - 为什么 C 编程中 scanf 命令的顺序很重要?
- lua - VLC time2clip.lua,将当前时间戳复制到剪贴板的扩展格式错误
- flutter - Dartz 接口无效覆盖
- reactjs - 你可以在云中运行 WebpackDevServer 吗?
- javascript - 将字符串中的数值相加
- http - 客户端未使用 websocket 协议:在“连接”标头中找不到“升级”令牌
- excel - 尝试将变量分配给 VBProject 时,如何解决 Excel 2016 在没有消息的情况下停止的问题?
- google-bigquery - BigQuery:对具有不同字段顺序的重复字段进行联合
- javascript - Mongoose JS,我的记分牌实现表现