如何通过jq从多个文件合并JSON数组并保留顺序

时间:2018-12-01 01:38:11

标签: json bash jq

我正在尝试设置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 :(得分:1)

unique_by需要进行排序,因此最简单的方法是使用INDEX(大写):

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

我在这里使用inputs来避免-s选项的所有缺点,但请记住改用-n选项。

如果您的jq没有INDEX,则可以使用以下def:

def INDEX(s; f):
  reduce s as $x  (null; .[$x|f] = $x );

答案 1 :(得分:0)

最简单的方法是从键到值构建一个映射(依次组合所有输入源),然后返回并重新处理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"  }
]