将两个复杂的JSON对象与数组合并

时间:2019-01-22 10:31:48

标签: json merge jq

我有以下两个json作为输入:

{
  "one": {
    "vars": [
      {
        "name": "a",
        "value": "a"
      },
      {
        "name": "b",
        "value": "b"
      }
    ]
  },
  "two": {
    "vars": [
      {
        "name": "c",
        "value": "c"
      },
      {
        "name": "d",
        "value": "d"
      }
    ]
  },
  "extras": "whatever"
}
{
  "one": {
    "vars": [
      {
        "name": "e",
        "value": "e"
      },
      {
        "name": "f",
        "value": "f"
      }
    ]
  },
  "two": {
    "vars": [
      {
        "name": "g",
        "value": "g"
      },
      {
        "name": "h",
        "value": "h"
      }
    ]
  }
}

我想将它们合并,以获得以下结果,其中每个部分的每个vars数组都合并在一起:

{
  "one": {
    "vars": [
      {
        "name": "a",
        "value": "a"
      },
      {
        "name": "b",
        "value": "b"
      },
      {
        "name": "e",
        "value": "e"
      },
      {
        "name": "f",
        "value": "f"
      }
    ]
  },
  "two": {
    "vars": [
      {
        "name": "c",
        "value": "c"
      },
      {
        "name": "d",
        "value": "d"
      },
      {
        "name": "g",
        "value": "g"
      },
      {
        "name": "h",
        "value": "h"
      }
    ]
  },
  "extras": "whatever"
}

理想但非必需:

  • 键(此处为onetwo)将是任意的,并且可能存在未定义的数量。
  • vars数组将不包含重复项(基于name),并且将使用优先级优先来覆盖第一个数组中的值。

我使用以下命令设法合并了两个对象和只有1个数组,但是密钥是硬编码的,因此我有点卡住了:

jq -s '.[0].one.vars=([.[].one.vars]|flatten)|.[0]' file1.json file2.json

2 个答案:

答案 0 :(得分:2)

jq -n 'input as $b | input
| .one.vars |= . + $b.one.vars
| .two.vars |= . + $b.two.vars' file2.json file1.json

file1.json必须紧跟file2.json之后才能保留extras

答案 1 :(得分:2)

首先,这是一个不使用顶级键名的解决方案,但它并不试图避免重复:

$A
| reduce keys_unsorted[] as $k (.;
    if .[$k] | (type == "object") and has("vars")
    then (.[$k]|.vars) += ($B[$k]|.vars) else . end )

这里$ A和$ B当然是指两个对象。您可以通过几种方式设置$ A和$ B。

如果您想对顶级键进行重新排序,只需使用指定顺序的过滤器即可扩展上述内容,例如:{extras, two, one}

为避免重复,我建议编写一个辅助函数来做到这一点,如以下部分所示。

避免重复

def extend(stream):
  reduce stream as $s (.;
    (map(.name) | index($s|.name)) as $i
    | if $i then .[$i] += $s
      else . + [$s]
      end) ;


$A
| reduce keys_unsorted[] as $k (.;
    if .[$k] | (type == "object") and has("vars")
    then (.[$k].vars) = ( .[$k].vars | extend(($B[$k].vars[])))
    else . end
  )