首页 > 解决方案 > 解析Json时如何解决Circe中的递归解码?

问题描述

我想使用 Circa 解析 JSON 字符串。您可以在下面找到输入 JSON 的示例。

这是一种递归数据。所以我的属性entity包含实体的依赖关系。

我想将依赖项解析为 map Map[String, Tasks]

{
  "entity": [
    {
      "task_id": "X",
      "type": "test",
      "attributes": {
        "name": "A",
        "random_property_count": 1 // should be ignored
      },
      "dependencies": {
        "random_name_1": {
          "entity": [
            {
              "task_id": "907544AF",
              "type": "test",
              "attributes": {
                "name": "B",
                "random_attribute": "*"
              },
              "dependencies": {
                "random_name_2": {
                  "entity": [
                    {
                      "task_id": "5",
                      "random_prop": "...",  // should be ignored as it's not present in model
                      "type": "test",
                      "attributes": {
                        "name": "C"
                      }
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    }
  ]
}

这是我的代码:

  case class Tasks (entity: Seq[Task])
  case class Task(task_id: String, `type`: String, attributes: Attributes, dependencies: Map[String, Tasks])
  case class Attributes(name: String)

  implicit val decodeTask: Decoder[Task] = deriveDecoder[Task]
  implicit val decodeTasks: Decoder[Tasks] = deriveDecoder[Tasks]
  implicit val decodeAttributes: Decoder[Attributes] = deriveDecoder[Attributes]

  val json = fromInputStream(getClass.getResourceAsStream("/json/example.json")).getLines.mkString
  val tasks = decode[Tasks](json)

  tasks match {
    case Left(failure) => println(failure)
    case Right(json)   => println(json)
  }

当我尝试将 JSON 字符串解析为我的模型时,我收到如下错误:

DecodingFailure(Attempt to decode value on failed cursor, List(DownField(dependencies), DownArray, DownField(entity), DownField(random_name_2), DownField(dependencies), DownArray, DownField(entity), DownField(random_name_1), DownField(dependencies), DownArray, DownField(entity)))

可能是什么问题?

标签: jsonscalarecursive-datastructurescirce

解决方案


的第二个成员在DecodingFailure这种情况下很有用,因为它提供了失败之前成功操作的历史以及失败操作本身(按时间倒序排列,最近的在前)。您可以像这样打印历史记录(或者只是在 的字符串表示中检查它DecodingFailure):

scala> import io.circe.DecodingFailure
import io.circe.DecodingFailure

scala> io.circe.jawn.decode[Tasks](doc) match {
     |   case Left(DecodingFailure(_, history)) => history.reverse.foreach(println)
     | }
DownField(entity)
DownArray
DownField(dependencies)
DownField(random_name_1)
DownField(entity)
DownArray
DownField(dependencies)
DownField(random_name_2)
DownField(entity)
DownArray
DownField(dependencies)

如果您按照这些步骤进入文档直到最后一个,您将获得以下对象:

{
  "task_id": "5",
  "random_prop": "...",
  "type": "test",
  "attributes": {
    "name": "C"
  }
}

最后一步是失败的,它是DownField(dependencies),这是有道理的,因为该对象没有dependencies字段。

有几种方法可以解决此问题。第一个是更改您的 JSON 表示,以便entity数组中的每个对象都有一个dependencies字段,即使它只是"dependencies": {}. 如果您不想或不能更改您的 JSON,您可以让该dependencies成员成为一个Option[Map[String, Tasks]](我刚刚确认这特别适用于您的情况)。您还可以定义一个自定义Map解码器,将缺失的字段解码为空地图,但这是一种更具侵入性的方法,我个人不建议这样做。


推荐阅读