Как объединить два объекта JSON, используя JQ? - PullRequest
0 голосов
/ 10 октября 2018

У меня есть два файла JSON.Я хочу добавить два массива SomeFile2.json к SomeFile1.json, как показано ниже.

SomeFile1.json

[
  {
    "DNSName": "CLB-test-112a877451.ap-northeast-1.elb.amazonaws.com",
    "Instances": [
      {
        "InstanceId": "i-0886ed703de64028a"
      }
    ]
  },
  {
    "DNSName": "CLB-test1-156925981.ap-northeast-1.elb.amazonaws.com",
    "Instances": [
      {
        "InstanceId": "i-0561634c4g3b4fa25"
      }
    ]
  }
]

SomeFile2.json

[
  {
    "InstanceId": "i-0886ed703de64028a",
    "State": "InService"
  },
  {
    "InstanceId": "i-0561634c4g3b4fa25",
    "State": "InService"
  }
]

Я хочу получить результат, как показано ниже:

[
  {
    "DNSName": "CLB-test-112a877451.ap-northeast-1.elb.amazonaws.com",
    "Instances": [
      {
        "InstanceId": "i-0886ed703de64028a"
        "State": "InService"
      }
    ]
  },
  {
    "DNSName": "CLB-test1-156925981.ap-northeast-1.elb.amazonaws.com",
    "Instances": [
      {
        "InstanceId": "i-0561634c4g3b4fa25"
        "State": "InService"
      }
    ]
  }
]

Я обрабатываю в оболочке bash через jq.Но безуспешно.

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

Поскольку содержимое второго файла, очевидно, предназначено для определения соответствия между InstanceId и State, давайте начнем с гипотезы следующего вызова jq:

jq --argfile dict SomeFile2.json -f program.jq SomeFile1.json

Далее, давайте создадим подходящий словарь:

reduce $dict[] as $x ({}; . + ($x|{(.InstanceId): .State}))) as $d

Теперь все остальное легко:

map(.Instances |= map(. + {State: $d[.InstanceId]}))

Соединение частей в program.jq:

(reduce $dict[] as $x ({}; . + ($x|{(.InstanceId): .State}))) as $d
| map(.Instances |= map(. + {State: $d[.InstanceId]}))

Альтернативы

Словарькак указано выше, можно создать без использования reduce следующим образом:

($dict | map( {(.InstanceId): .State}) | add) as $d

Другой альтернативой является использование INDEX/2:

(INDEX($dict[]; .InstanceId) | map_values(.State))) as $d

Если ваш jq не имеет INDEX/2Вы можете перехватить его def от https://raw.githubusercontent.com/stedolan/jq/master/src/builtin.jq

0 голосов
/ 10 октября 2018

Поскольку я нахожу довольно сложным, я начал процедурно: с помощью модуля json в ruby:

ruby -rjson -e '
  states = JSON.parse(File.read(ARGV.shift)).map {|o| [o["InstanceId"], o["State"]]}.to_h
  data = JSON.parse(File.read(ARGV.shift))
  data.each do |obj|
    obj["Instances"].each do |instance|
      instance["State"] = states[instance["InstanceId"]] || "unknown"
    end
  end
  puts JSON.pretty_generate data
' SomeFile2.json SomeFile1.json 

Но нам нужен jq, поэтому после некоторых проб и ошибок, инайти это в руководстве: https://stedolan.github.io/jq/manual/#Complexassignments - (заметьте, я изменил состояние для одного из экземпляров, чтобы лучше проверить вывод)

$ cat SomeFile2.json 
[
  {
    "InstanceId": "i-0886ed703de64028a",
    "State": "InService"
  },
  {
    "InstanceId": "i-0561634c4g3b4fa25",
    "State": "NOTInService"
  }
]

Сначала извлеките состояния вобъект, отображающий идентификатор в состояние:

$ state_map=$( jq -c 'map({"key":.InstanceId, "value":.State}) | from_entries' SomeFile2.json )
$ echo "$state_map"
{"i-0886ed703de64028a":"InService","i-0561634c4g3b4fa25":"NOTInService"}

Затем обновите экземпляры в первом файле:

jq --argjson states "$state_map" '.[].Instances[] |= . + {"State": ($states[.InstanceId] // "unknown")}' SomeFile1.json
[
  {
    "DNSName": "CLB-test-112a877451.ap-northeast-1.elb.amazonaws.com",
    "Instances": [
      {
        "InstanceId": "i-0886ed703de64028a",
        "State": "InService"
      }
    ]
  },
  {
    "DNSName": "CLB-test1-156925981.ap-northeast-1.elb.amazonaws.com",
    "Instances": [
      {
        "InstanceId": "i-0561634c4g3b4fa25",
        "State": "NOTInService"
      }
    ]
  }
]
...