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

时间:2018-12-07 01:13:37

标签: arrays json object merge jq

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

orig.json:

{
        "a": {
                "a1": "original a1",
                "a2": "original a2",
                "list": ["baz", "bar"]
        },
        "b": "original value B"
}

patch.json:

{
    "a": {
            "a1": "patch a1",
            "list": ["foo"]
    },
    "c": "original c"
}

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

{
  "a": {
    "a1": "patch a1",
    "a2": "original a2",
    "list": [
      "foo"
    ]
  },
  "b": "original value B",
  "c": "original c"
}

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

是否可以通过重写数组的默认合并策略轻松地使用jq做到这一点?

3 个答案:

答案 0 :(得分:1)

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

# Recursively meld a and b,
# concatenating arrays and
# favoring b when there is a conflict 
def meld(a; 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;

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"
}

答案 1 :(得分:0)

调用:

jq -f patch.jq --argfile patch patch.json  orig.json

其中patch.jq包含:

(.a.list + $patch.a.list) as $a
| . + $patch
| .a.list = $a

产生:

{
  "a": {
    "a1": "patch a1",
    "list": [
      "baz",
      "bar",
      "foo"
    ]
  },
  "b": "original value B",
  "c": "original c"
}

答案 2 :(得分:0)

$ 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