JQ库:用另一个对象键覆盖JSON对象的现有键



我是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";从不同的文档复制来自相同密钥的值";方法会更复杂,但让我们等待其他答案。我很确定有办法,但我太笨了:(它可能在某种程度上涉及reducepath/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中的现有值。要处理nullfalse,您需要更聪明:

. 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)
)
)

相关内容

  • 没有找到相关文章

最新更新