我在shell脚本中使用jq工具(jq-json-processor)解析json。
我有2个json文件,想合并成一个独特的文件
这里是文件的内容:
file1
{
"value1": 200,
"timestamp": 1382461861,
"value": {
"aaa": {
"value1": "v1",
"value2": "v2"
},
"bbb": {
"value1": "v1",
"value2": "v2"
},
"ccc": {
"value1": "v1",
"value2": "v2"
}
}
}
file2
{
"status": 200,
"timestamp": 1382461861,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
},
"bbb": {
"value3": "v3"
},
"ddd": {
"value3": "v3",
"value4": 4
}
}
}
预期的结果
{
"value": {
"aaa": {
"value1": "v1",
"value2": "v2",
"value3": "v3",
"value4": 4
},
"bbb": {
"value1": "v1",
"value2": "v2",
"value3": "v3"
},
"ccc": {
"value1": "v1",
"value2": "v2"
},
"ddd": {
"value3": "v3",
"value4": 4
}
}
}
我尝试了很多组合,但我得到的唯一结果是以下,这不是预期的结果:
{
"ccc": {
"value2": "v2",
"value1": "v1"
},
"bbb": {
"value2": "v2",
"value1": "v1"
},
"aaa": {
"value2": "v2",
"value1": "v1"
}
}
{
"ddd": {
"value4": 4,
"value3": "v3"
},
"bbb": {
"value3": "v3"
},
"aaa": {
"value4": 4,
"value3": "v3"
}
}
使用该命令:
jq -s '.[].value' file1 file2
从1.4开始,现在可以使用*操作符实现这一点。当给定两个对象时,它将递归地合并它们。例如,
jq -s '.[0] * .[1]' file1 file2
重要:注意-s(——slurp)标志,它将文件放在同一个数组中。
会让你:
{
"value1": 200,
"timestamp": 1382461861,
"value": {
"aaa": {
"value1": "v1",
"value2": "v2",
"value3": "v3",
"value4": 4
},
"bbb": {
"value1": "v1",
"value2": "v2",
"value3": "v3"
},
"ccc": {
"value1": "v1",
"value2": "v2"
},
"ddd": {
"value3": "v3",
"value4": 4
}
},
"status": 200
}
如果你也想去掉其他键(就像你期望的结果一样),一种方法是这样做:
jq -s '.[0] * .[1] | {value: .value}' file1 file2
或者假设更有效(因为它没有合并任何其他值):
jq -s '.[0].value * .[1].value | {value: .}' file1 file2
我不想放弃之前的非唯一键在我的对象
jq -n '{a:1, c:2}, {b:3, d:4}, {a:5,d:6}' |
jq -s 'map(to_entries)|flatten|group_by(.key)|map({(.[0].key):map(.value)|add})|add'
{
"a": 6,
"b": 3,
"c": 2,
"d": 10
}
或者,如果您只想保留一个值的数组,可以在提取值映射(.value)|——a——d——d——之后删除add
jq -n '{a:1, c:2}, {b:3, d:4}, {a:5,d:6}' |
jq -s 'map(to_entries)|flatten|group_by(.key)|map({(.[0].key):map(.value)})|add'
{
"a": [1, 5],
"b": [3],
"c": [2],
"d": [4, 6]
}
尝试删除命令的每个部分,并查看每个步骤如何修改对象数组…也就是运行这些步骤,看看输出如何变化
map(to_entries)
map(to_entries)|flatten
map(to_entries)|flatten|group_by(.key)
map(to_entries)|flatten|group_by(.key)|map({(.[0].key):map(.value)})
map(to_entries)|flatten|group_by(.key)|map({(.[0].key):map(.value)})|add
到目前为止,没有任何解决方案或评论考虑使用输入访问第二个文件。使用它将不需要构建额外的结构来从中提取,例如在使用——slurp(或-s)选项时使用包含所有内容的数组,这在几乎所有其他方法中都有。
要在顶层合并两个文件,只需将输入中的第二个文件添加到第一个文件中。使用+:
jq '. + input' file1.json file2.json
要在所有级别上递归合并两个文件,使用*作为操作符进行相同的操作:
jq '. * input' file1.json file2.json
也就是说,要递归合并你的两个文件,将两个对象缩小到它们的值字段,首先使用{value}过滤它们:
jq '{value} * (input | {value})' file1.json file2.json
{
"value": {
"aaa": {
"value1": "v1",
"value2": "v2",
"value3": "v3",
"value4": 4
},
"bbb": {
"value1": "v1",
"value2": "v2",
"value3": "v3"
},
"ccc": {
"value1": "v1",
"value2": "v2"
},
"ddd": {
"value3": "v3",
"value4": 4
}
}
}
Demo
请注意,只有在合并后才减少的解决方案,如。* input | {value} would,代码更短,但再次复活了“要提取的额外结构的积累”,这可能会产生大量的开销,如果最终被切断的部分变得很大。
为了操作两个以上的文件,要么相应地多次使用输入,要么以编程方式使用输入遍历所有文件,如
jq 'reduce inputs as $i (.; . * $i)' file*.json
注意,在这两种情况下,第一个文件总是通过输入上下文访问。而input(s)只处理剩下的文件,即从第二个文件开始(当然,除非给出——null-input或-n选项)。
从1.4开始,现在可以使用*操作符实现这一点。当给定两个对象时,它将递归地合并它们。例如,
jq -s '.[0] * .[1]' file1 file2
重要:注意-s(——slurp)标志,它将文件放在同一个数组中。
会让你:
{
"value1": 200,
"timestamp": 1382461861,
"value": {
"aaa": {
"value1": "v1",
"value2": "v2",
"value3": "v3",
"value4": 4
},
"bbb": {
"value1": "v1",
"value2": "v2",
"value3": "v3"
},
"ccc": {
"value1": "v1",
"value2": "v2"
},
"ddd": {
"value3": "v3",
"value4": 4
}
},
"status": 200
}
如果你也想去掉其他键(就像你期望的结果一样),一种方法是这样做:
jq -s '.[0] * .[1] | {value: .value}' file1 file2
或者假设更有效(因为它没有合并任何其他值):
jq -s '.[0].value * .[1].value | {value: .}' file1 file2