我有两个JSON文件:
source.json:
{
"general": {
"level1": {
"key1": "x-x-x-x-x-x-x-x",
"key3": "z-z-z-z-z-z-z-z",
"key4": "w-w-w-w-w-w-w-w"
},
"another" : {
"key": "123456",
"comments": {
"one": "111",
"other": "222"
}
}
},
"title": "The best"
}
和
target.json:
{
"general": {
"level1": {
"key1": "xxxxxxxx",
"key2": "yyyyyyyy",
"key3": "zzzzzzzz"
},
"onemore": {
"kkeeyy": "0000000"
}
},
"specific": {
"stuff": "test"
},
"title": {
"one": "one title",
"other": "other title"
}
}
考虑到所有级别,我需要两个文件中都存在的键的所有值,并将它们从 source.json 复制到 target.json 。
我已经查看并测试了this post的解决方案。
它仅复制第一级密钥,而我无法让它做我需要的事情。
this post中解决方案的结果如下:
{
"general": {
"level1": {
"key1": "x-x-x-x-x-x-x-x",
"key3": "z-z-z-z-z-z-z-z",
"key4": "w-w-w-w-w-w-w-w"
},
"another": {
"key": "123456",
"comments": {
"one": "111",
"other": "222"
}
}
},
"specific": {
"stuff": "test"
},
"title": "The best"
}
“常规”键下的所有内容均按原样复制。
我需要的是这个
{
"general": {
"level1": {
"key1": "x-x-x-x-x-x-x-x",
"key2": "yyyyyyyy",
"key3": "z-z-z-z-z-z-z-z"
},
"onemore": {
"kkeeyy": "0000000"
}
},
"specific": {
"stuff": "test"
},
"title": {
"one": "one title",
"other": "other title"
}
}
仅应复制“ key1”和“ key3”。
不得删除目标JSON 中的密钥,也不应创建新密钥。
有人可以帮忙吗?
答案 0 :(得分:1)
您可以采用的一种方法是,获取每个输入的所有标量值的所有路径,并采用交集。然后从这些路径中将值从源复制到目标。
首先,我们需要一个相交函数(很难制作):
def set_intersect($other):
(map({ ($other[] | tojson): true }) | add) as $o
| reduce (.[] | tojson) as $v ({}; if $o[$v] then .[$v] = true else . end)
| keys_unsorted
| map(fromjson);
然后进行更新:
$ jq --argfile s source.json '
reduce ([paths(scalars)] | set_intersect([$s | paths(scalars)])[]) as $p (.;
setpath($p; $s | getpath($p))
)
' target.json
答案 1 :(得分:1)
[注意:此回复针对原始数据回答了原始问题。 OP可能会考虑路径而不是键。]
无需计算交集即可获得合理有效的解决方案。
首先,让我们假设jq的以下调用:
jq -n --argfile source source.json --argfile target target.json -f copy.jq
在文件copy.jq中,我们可以先定义一个辅助函数:
# emit an array of the distinct terminal keys in the input entity
def keys: [paths | .[-1] | select(type=="string")] | unique;
为了检查到$source
的叶元素的所有路径,我们可以使用tostream
:
($target | keys) as $t
| reduce ($source|tostream|select(length==2)) as [$p,$v]
($target;
if $t|index($p[-1]) then setpath($p; $v) else . end)
由于$ t已排序,因此(至少在理论上)使用bsearch
代替index
是有意义的:
bsearch($p[-1]) > -1
此外,我们可以使用tostream
代替paths(scalars)
。
将这些替代品放在一起:
($target | keys) as $t
| reduce ($source|paths(scalars)) as $p
($target;
if $t|bsearch($p[-1]) > -1
then setpath($p; $source|getpath($p))
else . end)
{
"general": {
"level1": {
"key1": "x-x-x-x-x-x-x-x",
"key2": "yyyyyyyy",
"key3": "z-z-z-z-z-z-z-z"
},
"onemore": {
"kkeeyy": "0000000"
}
},
"specific": {
"stuff": "test"
}
}
答案 2 :(得分:0)
以下内容为修订后的问题提供了解决方案,该问题实际上是关于“路径”而不是“键”的。
([$target|paths(scalars)] | unique) as $paths
| reduce ($source|paths(scalars)) as $p
($target;
if $paths | bsearch($p) > -1
then setpath($p; $source|getpath($p))
else . end)
unique
被调用,以便随后可以使用二进制搜索。
jq -n --argfile source source.json --argfile target target.json -f program.jq