Как объединить JSON-массивы из нескольких файлов через jq с сохранением порядка - PullRequest
0 голосов
/ 01 декабря 2018

Я пытаюсь настроить иерархию файлов конфигурации JSON, где значения по умолчанию могут быть переопределены на нескольких уровнях.Каждый файл представляет собой набор пар ключ / значение.Многие из них организованы по функциям в не алфавитном порядке.

Я нашел способ получить желаемый логический результат, но он отсортирован в алфавитном порядке, и из-за количества ожидаемых параметров,пользователю будет трудно четко / быстро увидеть, является ли набор тем, что он хочет, прежде чем использовать его.

Есть ли способ выполнить такое объединение, заменив значения на соответствующих ключах и сохранив порядок?

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

default.json:

[
  { "ParameterKey": "FirstStackName",  "ParameterValue": "Production-App-Database" },
  { "ParameterKey": "SecondStackName", "ParameterValue": "Production-Directory" },
  { "ParameterKey": "ThirdStackName",  "ParameterValue": "Production-VPC" },
  { "ParameterKey": "FourthStackName", "ParameterValue": "AMIFunctions" },
  { "ParameterKey": "FifthKeyName",    "ParameterValue": "administrator" },
  { "ParameterKey": "SixthUserName",   "ParameterValue": "admin" },
  { "ParameterKey": "SeventhPassword", "ParameterValue": "" }
]

environment.json:

[
  { "ParameterKey": "FirstStackName",  "ParameterValue": "Development-App-Database" },
  { "ParameterKey": "SecondStackName", "ParameterValue": "Development-Directory" },
  { "ParameterKey": "ThirdStackName",  "ParameterValue": "Development-VPC" },
  { "ParameterKey": "FifthKeyName",    "ParameterValue": "developer" }
] 

user.json:

[
  { "ParameterKey": "FifthKeyName",    "ParameterValue": "jdoe" }
] 

secure.json:

[
  { "ParameterKey": "SeventhPassword", "ParameterValue": "secretvalue" }
] 

Если я запускаю это:

jq -s '.[3] + .[2] + .[1] + .[0] | unique_by(.ParameterKey)' default.json environment.json user.json secure.json

Я получаю что-то, что работает:

[
  { 
    "ParameterKey": "FifthKeyName",
    "ParameterValue": "jdoe"
  },
  {
    "ParameterKey": "FirstStackName",
    "ParameterValue": "Development-App-Database"
  },
  {
    "ParameterKey": "FourthStackName",
    "ParameterValue": "AMIFunctions"
  },
  {
    "ParameterKey": "SecondStackName",
    "ParameterValue": "Development-Directory"
  },
  {
    "ParameterKey": "SeventhPassword",
    "ParameterValue": "secretvalue"
  },
  {
    "ParameterKey": "SixthUserName",
    "ParameterValue": "admin"
  },
  {
    "ParameterKey": "ThirdStackName",
    "ParameterValue": "Development-VPC"
  }
]

Но (представьте60 параметров) это не так просто отсканировать как правильно, и я хочу вот что:

[
  { "ParameterKey": "FirstStackName",  "ParameterValue": "Development-App-Database" },
  { "ParameterKey": "SecondStackName", "ParameterValue": "Development-Directory" },
  { "ParameterKey": "ThirdStackName",  "ParameterValue": "Development-VPC" },
  { "ParameterKey": "FourthStackName", "ParameterValue": "AMIFunctions" },
  { "ParameterKey": "FifthKeyName",    "ParameterValue": "jdoe" },
  { "ParameterKey": "SixthUserName",   "ParameterValue": "admin" },
  { "ParameterKey": "SeventhPassword", "ParameterValue": "secretvalue" }
] 

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

unique_by влечет за собой сортировку, поэтому проще всего было бы использовать INDEX (верхний регистр):

[INDEX(inputs[]; .ParameterKey)[]]

Я использовал inputs здесь, чтобы избежать всех недостатков -sопцию, но не забудьте использовать опцию -n.

Если у вашего jq нет INDEX, вы можете использовать это значение:

def INDEX(s; f):
  reduce s as $x  (null; .[$x|f] = $x );
0 голосов
/ 01 декабря 2018

Легко сделать, чтобы построить карту из ключей к значениям (объединяя все входные источники по очереди), а затем вернуться и повторно обработать default.json, применяя эту сгенерированную карту.

jq -n --slurpfile template default.json '
# generate an unordered key:value dictionary
([inputs | .[] | {(.ParameterKey): (.ParameterValue)}] | add) as $map |

# apply those pairs to the template to get its ordering
[ $template[0][] | {"ParameterKey": (.ParameterKey), "ParameterValue": ($map[.ParameterKey])} ]

' default.json environment.json user.json secure.json

... который правильно выводит:

[
  {
    "ParameterKey": "FirstStackName",
    "ParameterValue": "Development-App-Database"
  },
  {
    "ParameterKey": "SecondStackName",
    "ParameterValue": "Development-Directory"
  },
  {
    "ParameterKey": "ThirdStackName",
    "ParameterValue": "Development-VPC"
  },
  {
    "ParameterKey": "FourthStackName",
    "ParameterValue": "AMIFunctions"
  },
  {
    "ParameterKey": "FifthKeyName",
    "ParameterValue": "jdoe"
  },
  {
    "ParameterKey": "SixthUserName",
    "ParameterValue": "admin"
  },
  {
    "ParameterKey": "SeventhPassword",
    "ParameterValue": "secretvalue"
  }
]

... сохраняя порядок default.json.


Если вы настаиваете на наличииПробельные символы просто так, один из способов сделать это - передать выходные данные вышеупомянутого через следующую команду:

#!/usr/bin/env bash
shopt -s extglob
pieces=( )
while read -r line; do
  case $line in
    "["|"]")       printf '%s\n' "$line";;
    *(' ')"}"?(,)) printf '%2s %-35s %s %2s\n' "${pieces[@]}" "$line"; pieces=( );;
    *)             pieces+=( "$line" )
  esac
done

Вышеприведенный код предназначен только для изменения пробелов и не распознает никаких разделителей, кроме символов новой строки;как таковой, его вывод всегда должен быть семантически идентичен его вводу.См. https://ideone.com/EI0RuJ как демонстрацию работы этого кода на практике с выводом:

[
 { "ParameterKey": "FirstStackName",   "ParameterValue": "Development-App-Database" },
 { "ParameterKey": "SecondStackName",  "ParameterValue": "Development-Directory" },
 { "ParameterKey": "ThirdStackName",   "ParameterValue": "Development-VPC" },
 { "ParameterKey": "FourthStackName",  "ParameterValue": "AMIFunctions" },
 { "ParameterKey": "FifthKeyName",     "ParameterValue": "jdoe" },
 { "ParameterKey": "SixthUserName",    "ParameterValue": "admin" },
 { "ParameterKey": "SeventhPassword",  "ParameterValue": "secretvalue"  }
]
...