Подстановочный путь в обходе JSON Цирцеи - PullRequest
0 голосов
/ 17 мая 2018

Рассмотрим этот JSON:

{
  "myDocument": {
    "variableName": {
            "attr1": "val1",
            "attr2": "val2"
        }
  }
}

Я хочу обработать документы JSON, которые будут иметь некоторые общие части.В частности, я хочу извлечь эти «общие» части, и я хочу использовать circe для этого случая.Я взглянул на руководство по обходу JSON и оптика , потому что буду иметь дело с глубоко вложенными структурами.

В предыдущем примере есть атрибут, который содержит объект.Документы, которые я собираюсь проанализировать, будут иметь атрибут variableName с разными именами, поэтому я не могу сделать что-то вроде этого:

root.myDocument.variableName.attr1

Есть ли способ, гдеЯ могу указать какой-то подстановочный знак?Примерно так:

root.myDocument.*.attr1

Я не могу контролировать все возможные комбинации имен, присутствующие в этом атрибуте, но я точно знаю, что атрибут будет присутствовать в каждом документе в этой точной позиции,Рассмотрите myDocument как фиксированный атрибут и variableName как фиксированный атрибут с различным именем в каждом документе.

Я не могу найти никакой информации об этом.Возможно ли это сделать?

Спасибо

1 Ответ

0 голосов
/ 17 мая 2018

Зная, что первый дочерний элемент myDocument является переменным элементом, и что вам нужны только attr1 и attr2 дочерние элементы myDocument s, вы можете сделать следующее:

import io.circe.Decoder
import io.circe._
import io.circe.jawn._

val in2 =
  """
    |[
    |   {
    |     "myDocument": {
    |       "firstUnknown": {
    |               "attr1": "val1",
    |               "ignored": "not_important",
    |               "attr2": "val2"
    |           }
    |     }
    |   },
    |   {
    |     "myDocument": {
    |       "secondUnknown": {
    |               "also_ignored": "not_important",
    |               "attr1": "val3",
    |               "attr2": "val4"
    |           }
    |     }
    |   },
    |   {
    |     "myDocument": {
    |       "thirdUnknown": {
    |               "attr1": "val5",
    |               "ignored2": "not_important",
    |               "attr2": "val6",
    |               "again_ignored": "not_important"
    |           }
    |     }
    |   }
    |]
  """.stripMargin

case class MyDocument(attr1 : String, attr2 : String)
object MyDocument{
  implicit val decoder : Decoder[MyDocument] = Decoder.instance{hCursor =>
    val firstChildKey = hCursor.downField("myDocument").keys.flatMap(_.headOption)

    firstChildKey.map{key =>
      for{
        attr1 <- hCursor.downField("myDocument").downField(key).downField("attr1").as[String]
        attr2 <- hCursor.downField("myDocument").downField(key).downField("attr2").as[String]
      } yield MyDocument(attr1,attr2)
    }.getOrElse(Left(DecodingFailure("Element is not a Json object or it's empty.",List())))
  }
}

val docs = parse(in2).flatMap(_.as[Vector[MyDocument]])

Идея состоит в том, что, как только мы переместим курсор на элемент myDocument, мы получим все keys, содержащиеся в объекте json, возьмем первый (который будет ключом элемента переменной, которым мы являемся).в), а затем используйте его, чтобы переместить курсор в нужную позицию для чтения attr1 и attr2.

Результат последней строки будет:

Right(Vector(MyDocument(val1,val2), MyDocument(val3,val4), MyDocument(val5,val6)))
...