jq:递归合并对象并连接数组

ted*_*edx 8 arrays merge json object jq

我有两个 json 文件,orig.json 和 patch.json,它们具有相似的格式。

原件.json:

{
        "a": {
                "a1": "original a1",
                "a2": "original a2",
                "list": ["baz", "bar"]
        },
        "b": "original value B"
}
Run Code Online (Sandbox Code Playgroud)

补丁.json:

{
    "a": {
            "a1": "patch a1",
            "list": ["foo"]
    },
    "c": "original c"
}
Run Code Online (Sandbox Code Playgroud)

目前我正在使用jq递归合并它们。但是,jq 对列表的默认行为只是重新分配。jq 使用的示例输出$ jq -s '.[0] * .[1]' orig.json patch.json

{
  "a": {
    "a1": "patch a1",
    "a2": "original a2",
    "list": [
      "foo"
    ]
  },
  "b": "original value B",
  "c": "original c"
}
Run Code Online (Sandbox Code Playgroud)

请注意,a.list现在等于 patch.json 的a.list。我希望新a.list的是 orig.json 的列表和 patch.json 的列表合并。换句话说,我想a.list等于["baz", "bar", "foo"].

有没有一种方法可以用 jq 轻松地做到这一点,也许是通过覆盖数组的默认合并策略?

Dir*_*ann 7

$ jq -s 'def deepmerge(a;b):
  reduce b[] as $item (a;
    reduce ($item | keys_unsorted[]) as $key (.;
      $item[$key] as $val | ($val | type) as $type | .[$key] = if ($type == "object") then
        deepmerge({}; [if .[$key] == null then {} else .[$key] end, $val])
      elif ($type == "array") then
        (.[$key] + $val | unique)
      else
        $val
      end)
    );
  deepmerge({}; .)' file1.json file2.json > merged.json
Run Code Online (Sandbox Code Playgroud)


pea*_*eak 6

这是一个通用函数,它通过在同一位置连接数组来递归组合两个复合 JSON 实体:

# Recursively meld a and b,
# concatenating arrays and
# favoring b when there is a conflict 
def meld(a; b):
  a as $a | b as $b
  | if ($a|type) == "object" and ($b|type) == "object"
    then reduce ([$a,$b]|add|keys_unsorted[]) as $k ({}; 
      .[$k] = meld( $a[$k]; $b[$k]) )
    elif ($a|type) == "array" and ($b|type) == "array"
    then $a+$b
    elif $b == null then $a
    else $b
    end;
Run Code Online (Sandbox Code Playgroud)

meld($orig; $patch) 的输出

将 $orig 设置为 orig.json 的内容并将 $patch 设置为 patch.json 的内容:

{
  "a": {
    "a1": "patch a1",
    "a2": "original a2",
    "list": [
      "baz",
      "bar",
      "foo"
    ]
  },
  "b": "original value B",
  "c": "original c"
}
Run Code Online (Sandbox Code Playgroud)

  • @KosProv:似乎运行的方式是“jq -f meld.jq 1.json 2.json ..”,函数定义在文件“meld.jq”中,后跟所需的调用语句(我使用了“reduce input”)如 $i (.; meld(.; $i))`,深度合并多个 JSON 文件而不用担心)...小心 **分号分隔参数**;逗号会产生误导性错误 `meld/1 is not Define at <top-level>, line 16:` (2认同)

pea*_*eak 0

调用:

jq -f patch.jq --argfile patch patch.json  orig.json
Run Code Online (Sandbox Code Playgroud)

其中 patch.jq 包含:

(.a.list + $patch.a.list) as $a
| . + $patch
| .a.list = $a
Run Code Online (Sandbox Code Playgroud)

产生:

{
  "a": {
    "a1": "patch a1",
    "list": [
      "baz",
      "bar",
      "foo"
    ]
  },
  "b": "original value B",
  "c": "original c"
}
Run Code Online (Sandbox Code Playgroud)