【问题标题】:How to resolve recursive decoding in Circe when parsing Json?解析Json时如何解决Circe中的递归解码?
【发布时间】:2019-09-21 05:12:47
【问题描述】:

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

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

我想解析依赖来映射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)))

可能是什么问题?

【问题讨论】:

    标签: json scala recursive-datastructures circe


    【解决方案1】:

    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 解码器,将缺失的字段解码为空地图,但这是一种更具侵入性的方法,我个人不建议这样做。

    【讨论】:

    • 选项按预期工作。感谢分享。
    猜你喜欢
    • 2011-05-05
    • 1970-01-01
    • 2018-09-30
    • 2017-05-16
    • 1970-01-01
    • 1970-01-01
    • 2019-08-23
    • 1970-01-01
    • 2019-09-15
    相关资源
    最近更新 更多