unix JQ库:使用其他对象键覆盖JSON对象的现有键

nxagd54h  于 2022-11-04  发布在  Unix
关注(0)|答案(3)|浏览(195)

我是JQ库的新手,我不知道如何在不从config.json复制任何其他属性的情况下,递归地用config.json和config_new.json中存在的键的值替换config_new.json中的值。
基本上具有:
第一个
因此,我尝试只使用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"]
  },
}

有人能帮我吗?

ttisahbt

ttisahbt1#

我想提出一些建议,但我不确定是否符合您的要求:不要合并JSON文档,而是将“目标”文档编写为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

“从不同文档的相同键中复制值”的方法会更复杂,但让我们等待其他答案。我很确定有一种方法,但我太笨了:)它可能在某种程度上涉及reducepath/getpath/setpath

zfciruhq

zfciruhq2#

下面是一个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作为“target”,将config. json作为“source”。要将$source合并到$target中,请遍历$target中的$source键,然后查看键值的数据类型:如果它是一个对象,那么递归地合并这些对象,否则将$source的值放入$target中。

0sgqnhkj

0sgqnhkj3#

好的,这是我认为可能的解决方案。如果有改进的空间,请留下评论。

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

请注意,config.json中的值“null”或“false”不会覆盖config_new.json中的现有值。要处理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)
  )
)

相关问题