[英]Circe list deserialization with best-attempt and error reporting
我正在使用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]
而是將其反序列化為List[Try[Clip]]
,其中每個項目都類似於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
則被解碼為OK:
Success(List(Failure(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(dog), DownField(properties)))), Success(Clip(pqr,def,terrier))))
而且,如果您在更高級別上破壞了某些東西,甚至無法檢索items
數組,那么您將在頂層得到Failure
:
Failure(ParseException: Couldn't get items)
不確定是否還有一種慣用的解決方案,但是我找不到解決方案,因此希望對您有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.