Как просмотреть список вложенных карт в scala

#json #scala #hashmap

Вопрос:

Мне была предоставлена строка json, которая выглядит следующим образом:

 {
  "dataflows": [
    {
  "name": "test",
  "sources": [
    {
      "name": "person_inputs",
      "path": "/data/input/events/person/*",
      "format": "JSON"
    }
  ],
  "transformations": [
    {
      "name": "validation",
      "type": "validate_fields",
      "params": {
        "input": "person_inputs",
        "validations": [
          {
            "field": "office",
            "validations": [
              "notEmpty"
            ]
          },
          {
            "field": "age",
            "validations": [
              "notNull"
            ]
          }
        ]
      }
    },
    {
      "name": "ok_with_date",
      "type": "add_fields",
      "params": {
        "input": "validation_ok",
        "addFields": [
          {
            "name": "dt",
            "function": "current_timestamp"


          }
        ]
      }
    }
  ],
  "sinks": [
    {
      "input": "ok_with_date",
      "name": "raw-ok",
      "paths": [
        "/data/output/events/person"
      ],
      "format": "JSON",
      "saveMode": "OVERWRITE"
    },
    {
      "input": "validation_ko",
      "name": "raw-ko",
      "paths": [
        "/data/output/discards/person"
      ],
      "format": "JSON",
      "saveMode": "OVERWRITE"
    }
 

И меня попросили использовать его как своего рода рецепт для конвейера ETL, т. Е. Данные должны быть извлечены из «пути», указанного в ключе «источники», преобразования, которые должны быть выполнены, указаны в ключе «преобразования», и, наконец, преобразованные данные должны быть сохранены в одном из двух указанных ключей «приемника».

Я решил преобразовать строку json в карту scala следующим образом:

 val json = Source.fromFile("path/to/json")
//parse
val parsedJson = jsonStrToMap(json.mkString)
implicit val formats = org.json4s.DefaultFormats
val parsedJson = parse(jsonStr).extract[Map[String, Any]]
 

итак, с помощью этого я получаю структуру, подобную этой:

введите описание изображения здесь

которая представляет собой карту, первым значением которой является список карт. Я могу оценить parsedJson("dataflows") , чтобы получить:

введите описание изображения здесь

как и ожидалось, это список, но тогда я не могу пройти через такой список, хотя мне это нужно, чтобы добраться до источников, преобразований и приемников. Я попытался использовать индекс списка, чтобы, например, получить его первый элемент , например: parsedJson("dataflows")(0) , но безрезультатно.

Кто-нибудь, пожалуйста, может помочь мне пересечь эту структуру? Любая помощь была бы вам очень признательна.

Овации,

Ответ №1:

При вычислении parsedJson("dataflows") a Tuple2 возвращается aka a Tuple , который имеет два элемента, к которым осуществляется доступ с ._1 помощью и ._2

Таким dataflows(1)._1 образом,возвращаемое значение является «источниками» и dataflows(1)._2 представляет собой список карт (Список[Карта[K, V]), которые могут быть пройдены, как вы обычно проходите элементы a List , где каждый элемент Map

Давайте разберем это, например:

val dataFlowsZero = ("sources", List(Map(42 -> "foo"), Map(42 -> "bar")))

Первый элемент в Tuple

 scala> dataFlowsZero._1
String = sources
 

Второй элемент в Tuple

 scala> dataFlowsZero._2
List[Map[Int, String]] = List(Map(42 -> foo), Map(42 -> bar))`
 

Сопоставьте ключи в каждом Map List из них с новым List

 scala> dataFlowsZero._2.map(m => m.keys)                                                                                                                                                                    
List[Iterable[Int]] = List(Set(42), Set(42))
 

Сопоставьте значения в каждом Map List из них с новым List

 scala> dataFlowsZero._2.map(m => m.values)
List[Iterable[String]] = List(Iterable(foo), Iterable(bar))
 

Ответ №2:

Лучшим решением является преобразование JSON в полную структуру данных, которая вам была предоставлена, а не просто Map[String, Any] . Это делает тривиальным выбор нужных вам данных. Например,

 val dataFlows = parse(jsonStr).extract[DataFlows]

case class DataFlows(dataflows: List[DataFlow])
case class DataFlow(name: String, sources: List[Source], transformations: List[Transformation], sinks: List[Sink])
case class Source(name: String, path: String, format: String)
case class Transformation(name: String, `type`: String, params: List[Param])
case class Param(input: String, validations: List[Validation])
case class Validation(field: String, validations: List[String])
case class Sink(input: String, name: String, paths: List[String], format: String, saveMode: String)
 

Идея состоит в том, чтобы заставить обработчик JSON выполнять большую часть работы по созданию типобезопасной версии исходных данных.