Как разрешить рекурсивное декодирование в Circe при разборе Json?

Я хочу проанализировать строку JSON, используя Circa. Вы можете найти пример ввода 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)))

В чем может быть проблема?


person Alex    schedule 21.09.2019    source источник


Ответы (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, который декодирует отсутствующее поле как пустую карту, но это гораздо более инвазивный подход, который я бы лично не рекомендовал.

person Travis Brown    schedule 21.09.2019
comment
Он работает, как и ожидалось, с опциями. Спасибо, что поделились этим. - person Alex; 22.09.2019