对于特定类型DecodeJson[A]
,我有一个A
的实例,我有一个Json
的实例,称之为j
。我想做j.as[List[A]]
之类的事情。但是,这个JSON来自第三方,如果数组中的某些项目缺少字段,我只想记录这些项目的错误并只收集可以正确解析的项目(而不是整个解码失败。)
在我看来,我最终想要的是像(List[(String, CursorHistory)], List[A])
这样的东西。也就是说,任何成功解码的元素都将在右侧列表中结束,并且任何无法成功解析的项目的错误将在左侧累积。这看起来很像scalaz中的MonadPlus.separate
方法。
我可以想到几种到达那里的方法。一种方法是做一些事情:
j.as[List[Json]].map(_.map(_.as[A].result).separate)
这应该根据需要给我一个DecodeResult[List[(String, CursorHistory)], List[A]]
。但是,这将丢弃有关失败项目所在的JSON数组中的位置的游标信息。我可以使用像zipWithIndex
这样的东西,但这开始变得非常混乱。
对我而言,似乎更好的解决方案是做这样的事情:
implicit def DecodeResultDecodeJson[A: DecodeJson]: DecodeJson[DecodeResult[A]] =
decodeArr(_.as[A]) // outer DecodeResult is always a success
j.as[List[DecodeResult[A]]].map(_.map(_.result)).separate)
这将为我提供相同的输出类型,但应填充CursorHistory
以包含有关单步执行数组元素的信息。
以下是一个例子:
val j = Json.array(List("1", "2", "foo").map(Json.jString): _*)
val r = j.as[List[DecodeResult[Int]]].map(_.map(_.result).separate)
// r is DecodeResult[(List[(String, CursorHistory)], List[Int])] =
// DecodeResult(\/-((List((Int,CursorHistory([->,->,\\]))),List(1, 2))))
这似乎表现得相当合理(忽略了可能发生的一些可怕的List连接)。但是,似乎这种事情是一个相当常见的用例,所以我想知道是否有更简单或内置的方法来做这样的事情。如果没有,我可以向DecodeJson[DecodeJson[A]]
实例的argonaut提交PR,看看是否有人感兴趣。
答案 0 :(得分:2)
您可以使用HCursor
解码器,而不是保留历史记录的Json
解码器:
j.as[List[HCursor]].map(_.map(_.as[Int].result).separate)