我想使用jq合并多个文件,并且如果多个文件包含名称相同的数组,则需要合并数组(顺序无关紧要)。
例如
file1
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{"param": 1}
]
}
file2
{
"status": 200,
"timestamp": 1382461861,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
},
"parameter" [
{"param": 2}
]
}
建议其他堆栈溢出文章合并这些json,我应该这样做:
jq -s '.[0] * .[1]' file1 file2
但是这让我得到了
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{
"param": 2
}
],
"status": 200,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
}
}
我想要的地方是
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{ "param": 1},
{ "param": 2}
],
"status": 200,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
}
}
请注意,“参数”数组应同时包含file1和file2中的元素
我还需要一种不需要指定数组字段的解决方案,并且可以将数组嵌套在json的任何级别
我愿意接受不使用jq的解决方案,我想可以使用一个小的python脚本
我找到的最接近的解决方案要求我知道参数是一个数组
jq -s '.[0] * .[1]' file1.json file2.json >temp.json
jq -s '.[0].parameter=([.[].parameter]|flatten)|.[0]' temp.json file1.json
,输出为
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{
"param": 2
},
{
"param": 1
}
],
"status": 200,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
}
}
答案 0 :(得分:1)
您基本上是在实现自己的合并方案。如果需要通用解决方案,则需要定义一个函数,以便可以递归地完成它。这与“ *
不太一样,但是具有不同的数组语义”,但是您可以使用如下代码:
def new_merge($item):
if type == ($item|type) then # if same types
if type == "array" then # concatenate the arrays
. + $item
elif type == "object" then # recursively merge objects
reduce ($item|to_entries[]) as {$key,$value} (.;
.[$key] |= new_merge($value)
)
else # just take the "other" value
$item // .
end
else # just take the "other" value
$item // .
end
;
我会将其放入您的~/.jq
文件中,并按以下方式调用:
$ jq 'reduce inputs as $i (.; do_merge($i))' file*.json
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{
"param": 1
},
{
"param": 2
}
],
"status": 200,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
}
}
另一方面,如果您希望像在原始问题中那样递归合并数组项,只需更改数组大小写以递归合并相应项即可。
def new_merge2($item):
if type == ($item|type) then
if type == "array" then
[.,$item] | transpose[] as [$a,$b] | [$a | new_merge2($b)]
elif type == "object" then
reduce ($item|to_entries[]) as {$key,$value} (.;
.[$key] |= new_merge2($value)
)
else
$item // .
end
else
$item // .
end
;
此版本将产生:
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{
"param1": 1,
"param2": 2
}
],
"status": 200,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
}
}
答案 1 :(得分:1)
在有两个文件(每个文件都有一个对象)的情况下,这是一个解决问题的方法,在该对象中至少有一个同名的数组值字段在某个级别上。
许多方面的要求不清楚,因此以下内容可能需要根据更详细的要求进行调整。如果文件多于两个,则可以使用相同的技术,但是详细信息将取决于详细要求。
jq -n --slurpfile file1 file1.json --slurpfile file2 file2.json '
# a and b are expected to be jq paths ending with a string
# emit the array of the intersection of key names
def common(a;b):
((a|map(.[-1])) + (b|map(.[-1])))
| unique;
$file1[0] as $f1
| $file2[0] as $f2
| [$f1 | paths as $p | select(getpath($p) | type == "array") | $p] as $p1
| [$f2 | paths as $p | select(getpath($p) | type == "array") | $p] as $p2
| $f1+$f2
| if ($p1|length) > 0 and ($p2|length) > 0
then common($p1; $p2) as $both
| if ($both|length) > 0
then first( $p1[] | select(.[-1] == $both[0])) as $p1
| first( $p2[] | select(.[-1] == $both[0])) as $p2
| ($f1 | getpath($p1)) as $a1
| ($f2 | getpath($p2)) as $a2
| setpath($p1; $a1 + $a2)
else .
end
else .
end
'
使用给定的输入,在第二个文件中添加缺少的“:”后,输出为:
{
"value1": 200,
"timestamp": 1382461861,
"parameter": [
{
"param1": 1
},
{
"param2": 2
}
],
"status": 200,
"value": {
"aaa": {
"value3": "v3",
"value4": 4
}
}
}
答案 2 :(得分:0)
在两个文件(每个文件有一个对象)的情况下,这是一个简单但通用的解决方案。
此解决方案将在同一路径上连接每对数组。希望它很简单,足以说明一切,并可以进行修改以应对各种更详细的要求。
jq -n --slurpfile file1 file1.json --slurpfile file2 file2.json '
$file1[0] as $f1
| $file2[0] as $f2
| reduce ($f1 | paths) as $p ($f1+$f2;
($f1|getpath($p)) as $v1
| ($f2|getpath($p)) as $v2
| if ($v1 | type == "array") and
($v2 | type == "array")
then setpath($p; $v1 + $v2)
else .
end)
'
输出完全符合要求,假设第二个文件已通过显而易见的方式更正,因此它是有效的JSON。