Как объединить json файлов с помощью jq? - PullRequest
3 голосов
/ 05 февраля 2020

У меня есть следующий json файл

[
  {
    "clusterName": "cluster1",
    "nodes": [
      {
        "hostname": "server1",
        "dse": "6.7.5"
      },
      {
        "hostname": "server2",
        "dse": "6.7.5"
      }
    ]
  },
  {
    "clusterName": "cluster2",
    "nodes": [
      {
        "hostname": "server3",
        "dse": "6.7.5"
      },
      {
        "hostname": "server4",
        "dse": "6.7.5"
      }
    ]
  }
]

И у меня есть еще один json

[
  {
    "hostname": "server1",
    "memorysize": "47.01 GiB",
    "processorcount": 12
  },
  {
    "hostname": "server2",
    "memorysize": "47.01 GiB",
    "processorcount": 12
  },
  {
    "hostname": "server3",
    "memorysize": "47.01 GiB",
    "processorcount": 10
  },
  {
    "hostname": "server4",
    "memorysize": "47.01 GiB",
    "processorcount": 11
  },
  {
    "hostname": "server5",
    "memorysize": "47.01 GiB",
    "processorcount": 12
  },
  {
    "hostname": "server6",
    "memorysize": "47.01 GiB",
    "processorcount": 12
  }
]

Я хочу объединить эти два jsons для получения следующего вывода

[
  {
    "clusterName": "cluster1",
    "nodes": [
      {
        "hostname": "server1",
        "dse": "6.7.5",
        "memorysize": "47.01 GiB",
        "processorcount": 12
      },
      {
        "hostname": "server2",
        "dse": "6.7.5",
        "memorysize": "47.01 GiB",
        "processorcount": 12
      }
    ]
  },
  {
    "clusterName": "cluster2",
    "nodes": [
      {
        "hostname": "server3",
        "dse": "6.7.5",
        "memorysize": "47.01 GiB",
        "processorcount": 10
      },
      {
        "hostname": "server3",
        "dse": "6.7.5",
        "memorysize": "47.01 GiB",
        "processorcount": 11
      }
    ]
  }
]

В основном первый файл имеет список словарей кластеров. с узлами, и у меня есть второй файл со списком словаря узлов.

Упомянутое решение не работает с несколькими кластерами.

Есть ли лучше сделать это в python вместо ?

Ответы [ 3 ]

2 голосов
/ 05 февраля 2020

Решение с использованием jq:

<file1 jq --slurpfile f file2 '
  {
     clusterName:.[].clusterName,
     nodes:map($f[],.nodes)|add|group_by(.hostname)|map(add)
  }'

Это построение объекта с использованием обоих файлов.
Первое поле clusterName берется из того же поля второго файла.
The второе поле nodes представляет собой комбинацию объектов обоих на основе hostname (выполнено с помощью команды group_by)


Предварительный ответ на комментарий ниже:
Я не думаю, что -s имеет здесь какие-либо преимущества, поскольку вам нужны оба файла в памяти (вместо 1 с --slurpfile).
Чтобы не играть с индексами, идея состоит в том, чтобы проверить, является ли поле существует или нет до его использования. Вы можете сделать это с помощью операторов ? и //. Вместе они из рода if not ... then .... Вот возможное решение:

jq -s '{
   clusterName:(.[][].clusterName?//empty),
   nodes:map(.[].nodes[]?//.[])|group_by(.hostname)|map(add)
}' file1 file2

Как видите, трудность в обоих скриптах состоит в том, чтобы "нормализовать" объекты для выполнения операции group_by.

0 голосов
/ 06 февраля 2020

вызов больше похож на cat file1 file2 | jq ...

Вот решение, которое предполагает, что все входные данные представлены в виде потока. Это решение также позволяет избежать использования параметра командной строки -s.

cat master.json hostnames.json | jq '
  # input: an array of objects, each with a "nodes" key
  def mergeNode($node): 
    map(if .hostname == $node.hostname then . + $node else . end);
  reduce inputs[] as $n (.; map_values( .nodes |= mergeNode($n) ))'

Обратите внимание, что параметр командной строки -n НЕ указан.

Это решение также позволяет использовать более одного файла "hostnames".

0 голосов
/ 06 февраля 2020

Я сделал это, используя python вместо

for cluster in clusters:
    for node in cluster["nodes"]:
        node.update(list(filter(lambda nodes: nodes['hostname'] == node['hostname'],nodes))[0])
...