argonaut - 在提取List [A]时分离失败和成功

时间:2014-11-19 16:10:09

标签: scala scalaz argonaut

对于特定类型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,看看是否有人感兴趣。

1 个答案:

答案 0 :(得分:2)

您可以使用HCursor解码器,而不是保留历史记录的Json解码器:

j.as[List[HCursor]].map(_.map(_.as[Int].result).separate)