简体   繁体   English

解析Json时如何解决Circe中的递归解码?

[英]How to resolve recursive decoding in Circe when parsing Json?

I want to parse a JSON string using Circa.我想使用 Circa 解析 JSON 字符串。 You can find an example of the input JSON below.您可以在下面找到输入 JSON 的示例。

It's a kind of recursive data.这是一种递归数据。 So my property entity contains dependencies of entities.所以我的属性entity包含实体的依赖关系。

I want to parse dependencies to map Map[String, Tasks] .我想解析对 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"
                      }
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    }
  ]
}

Here is my code:这是我的代码:

  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)
  }

When I try to parse JSON string to my model, I get an error like this:当我尝试将 JSON 字符串解析为 model 时,出现如下错误:

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)))

What can be the issue?可能是什么问题?

The second member of the DecodingFailure can be useful in cases like this, since it provides the history of successful operations that preceded the failure as well as the failing operation itself (in reverse chronological order, with the most recent first). DecodingFailure的第二个成员在这种情况下很有用,因为它提供了失败之前成功操作的历史以及失败操作本身(按时间倒序排列,最近的在前)。 You can print the history like this (or just inspect it in the string representation of the 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)

If you follow these steps into your document up until the last one, you'll get to the following object:如果您在文档中执行这些步骤直到最后一个,您将获得以下 object:

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

The last step is the one that failed, and it's DownField(dependencies) , which makes sense, given that this object doesn't have a dependencies field.最后一步是失败的,它是DownField(dependencies) ,这是有道理的,因为这个 object 没有dependencies项字段。

There are a couple of ways you could fix this issue.有几种方法可以解决此问题。 The first would be to change your JSON representation so that every object in an entity array has a dependencies field, even if it's just "dependencies": {} .第一个是更改您的 JSON 表示,以便entity数组中的每个 object 都有一个dependencies项字段,即使它只是"dependencies": {} If you don't want to or can't change your JSON, you could make the dependencies member an Option[Map[String, Tasks]] (I've just confirmed that this works for your case specifically).如果您不想或无法更改 JSON,您可以将dependencies项成员设为Option[Map[String, Tasks]] (我刚刚确认这特别适用于您的情况)。 You could also define a custom Map decoder that decodes a missing field as an empty map, but that's a much more invasive approach that I wouldn't personally recommend.您还可以定义一个自定义Map解码器,将缺失的字段解码为空 map,但这是一种更具侵入性的方法,我个人不推荐。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM