我是JQ库的新手,我不知道如何在不复制config.json中的任何其他属性的情况下,用config.json和config_new.json中都存在的键的值递归替换config_new.json中的值。
基本具备:
// config_new.json
{
"name": "testName",
"age": "tooOld",
"properties": {
"title": "Mr",
"fruits": ["apple", "banana"]
},
}
// config.json
{
"newName": "changedName",
"age": "tooYoung",
"properties": {
"title": "Mr",
"height": "tooTall",
"fruits": ["banana"]
},
"certificate": "present"
}
// expected result
{
"name": "testName",
"age": "tooYoung",
"properties": {
"title": "Mr",
"height": "tooTall",
"fruits": ["banana"]
},
}
因此,我试图只使用config.json中的已知值来覆盖config_new.json的值。
我试过使用
jq -s '.[0] * .[1]' config_new.json config.json
但这只是部分起作用,因为它还复制了config_new.json:中不存在的键值对
{
"name": "testName",
"newName": "changedName", // This should not be here
"age": "tooYoung", // This was replaced from config.json
"properties": {
"title": "Mr",
"height": "tooTall", // This should not be here
"fruits": ["banana"]
},
}
有人能帮我吗?
这里有一个jq答案,它根据您的标准递归地合并对象:
jq -s '
def merge($source):
. as $target
| reduce ($source | keys | map(select(in($target))))[] as $key ($target;
.[$key] = if (.[$key] | type) == "object"
then .[$key] | merge($source[$key])
else $source[$key]
end
)
;
. as [$new, $old]
| $new | merge($old)
' config_new.json config.json
输出
{
"name": "testName",
"age": "tooYoung",
"properties": {
"title": "Mr",
"fruits": [
"banana"
]
}
}
这将config_new.json作为";目标";config.json作为";来源";。要将$source合并到$target中,请迭代$target中的$source键,然后查看键值的数据类型:如果是对象,则递归地合并这些对象,否则将该键的$sources值放入$target中。
我想提出一些建议,但我不确定它是否适合您的需求:不要合并JSON文档,而是编写您的"目标";文档作为jq程序本身。
config_new.jq
:
{
"name": .name,
"age": .age,
"properties": {
"title": .properties.title,
"fruits": .properties.fruits
}
}
这读起来几乎像";真实的";JSON。
或者,如果你想减少重复:
{
name,
age,
properties: .properties | {
title,
fruits
}
}
然后将旧文件迁移到新格式:
jq -f config_new.jq config.json > config_new.json
A";从不同的文档复制来自相同密钥的值";方法会更复杂,但让我们等待其他答案。我很确定有办法,但我太笨了:(它可能在某种程度上涉及reduce
和path
/getpath
/setpath
。
好吧,这是我认为可能的解决方案。如果还有改进的空间,请留下评论。
$ jq --slurpfile cfg config.json '. as $new
| reduce (paths(scalars,arrays) | select(any(numbers)|not)) as $path (
{};
setpath($path; ($cfg[0]|getpath($path))//($new|getpath($path))))' config_new.json
{
"name": "testName",
"age": "tooYoung",
"properties": {
"title": "Mr",
"fruits": [
"banana"
]
}
}
它通过迭代所有不包含数组的路径来减少。细分:
. as $new # store original "new" json
| reduce (paths(scalars,arrays) | select(any(numbers)|not)) as $path ( # reduce over all paths of input (original "new" json) that are leaf paths or arrays (but not array elements)!
{}; # start with an empty result
setpath(
$path; # set "$path" in the result
($cfg[0]|getpath($path)) # query value from existing config (read via slurpfile)
// ($new|getpath($path)))) # if value does not exist or is null, use existing value (from original "new" json)
注意,值";空";或";false";则CCD_ 6中的值不会覆盖CCD_ 7中的现有值。要处理null
和false
,您需要更聪明:
. as $new
| [$cfg[0] | paths] as $cfg_paths # store all paths from config.json
| reduce (paths(scalars,arrays) | select(any(numbers)|not)) as $path (
{};
setpath(
$path;
if $path|IN($cfg_paths[]) # check if $path exists in config
then $cfg[0] else $new # use old or new config
end | getpath($path) # get $path from old/new config respectively
)
)
最终的if
也可以用select
滤波器表示:
. as $new
| [$cfg[0] | paths] as $cfg_paths
| reduce (paths(scalars,arrays) | select(any(numbers)|not)) as $path (
{};
setpath(
$path;
$cfg[0] | select($path|IN($cfg_paths[])) // $new # $cfg if path exists, otherwise $new (equivalent to "if" above)
| getpath($path)
)
)