Как разрешить рекурсивное декодирование в Circe при разборе Json? - PullRequest
1 голос
/ 21 сентября 2019

Я хочу проанализировать строку 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)))

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

1 Ответ

2 голосов
/ 21 сентября 2019

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

...