【问题标题】:Circe list deserialization with best-attempt and error reportingCirce 列表反序列化与最佳尝试和错误报告
【发布时间】:2019-02-17 12:03:36
【问题描述】:

我正在使用 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" 说),那么整个反序列化将失败 - 它不会给我未损坏的列表 @987654326 @ items,它只是告诉我它失败了。

因此,我不想反序列化为List[Clip],而是想反序列化为List[Try[Clip]],其中每个项目要么像Success(Clip(xyz,abc,spaniel)),要么像Failure(ErrorDescriptionForThatItem)

我能够在 Argonaut 中实现这一点(使用一些相当丑陋的代码),但无法弄清楚 Circe 中的语法。实现这一目标的最佳方法是什么? 谢谢!

【问题讨论】:

    标签: json scala circe argonaut


    【解决方案1】:

    好的,所以这个解决方案有效:

    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

    Failure(ParseException: Couldn't get items)
    

    不确定是否有更惯用的解决方案,但我找不到,所以我希望这对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-07
      • 1970-01-01
      • 2014-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多