【问题标题】:jq combine duplicates with nested objectsjq 将重复项与嵌套对象结合起来
【发布时间】:2026-01-14 01:15:01
【问题描述】:

在过去的几天里,我搜索了类似的问题/答案,试图解决这个问题,我相信我的业余 jq 技能正在阻止我解决这个问题。

我正在尝试合并重复的条目;例如...我想:

{
  "Version": "2008-10-17",
  "Id": "SomeBucketPolicy",
  "Statement": [
    {
      "Sid": "Stmt1234567890987",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::726481726312:root"
      },
      "Action": [
        "s3:GetBucketAcl",
        "s3:GetBucketPolicy"
      ],
      "Resource": "arn:aws:s3:::it-lab-test"
    },
    {
      "Sid": "Stmt3423424566754",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::726481726312:root"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::it-lab-test/*"
    },
    {
      "Sid": "SomeAPIUser",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::536415397313:user/SomeAPIUser"
      },
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:GetObjectRetention"
      ],
      "Resource": "arn:aws:s3:::it-lab-test/*"
    },
    {
      "Sid": "SomeAPIUser",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::536415397313:user/SomeAPIUser"
      },
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:GetObjectTagging"
      ],
      "Resource": [
        "arn:aws:s3:::it-lab-test/*",
        "arn:aws:s3:::another-test-bucket/*",
        "arn:aws:s3:::someother-test-bucket/*"
      ]
    }
  ]
}

...变成:

{
  "Version": "2008-10-17",
  "Id": "SomeBucketPolicy",
  "Statement": [
    {
      "Sid": "Stmt1234567890987",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::726481726312:root"
      },
      "Action": [
        "s3:GetBucketAcl",
        "s3:GetBucketPolicy"
      ],
      "Resource": "arn:aws:s3:::it-lab-test"
    },
    {
      "Sid": "Stmt3423424566754",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::726481726312:root"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::it-lab-test/*"
    },
    {
      "Sid": "SomeAPIUser",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::536415397313:user/SomeAPIUser"
      },
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:GetObjectRetention",
        "s3:GetObjectTagging"
      ],
      "Resource": [
        "arn:aws:s3:::it-lab-test/*",
        "arn:aws:s3:::another-test-bucket/*",
        "arn:aws:s3:::someother-test-bucket/*"
      ]
    }
  ]
}

我希望它尽可能灵活和宽容;如果需要为多个条目创建数组,如果嵌套对象中有重复项,它们也会被正确合并,等等。

我尝试了多种方法,使用了大量示例/技术(分组、映射、使用函数),但我无法得出我正在寻找的结果(我要么重复或数据不再存在且未合并)。我最接近的是通过使用这个解决方案Remove duplicate values from JSON with jq ...但是在处理嵌套在块中的多个对象时遇到了问题。

中的任何帮助将不胜感激

【问题讨论】:

  • 告诉我们你尝试了什么。你是什​​么意思重复?精心制作

标签: json object merge jq


【解决方案1】:

由于没有关于合并算法的具体要求 已经给出,这个回应将集中在一个架构上 解决问题提出的问题类别。

不过,为了说明和具体的目的,一个可交换的成对合并函数将定义如下:

def merge(a; b):
  def merge_objects($x;$y):
    (($x|keys_unsorted) + ($y|keys_unsorted) | unique) as $keys
    | reduce $keys[] as $k (null; . + {($k): merge($x[$k]; $y[$k])});
  if a == b then a
  elif a == null then b
  elif b == null then a
  elif (a|type) | (. == (b|type)) and (. == "object") 
    then merge_objects(a;b)
  elif (a|type == "array") and (b|type) == "array"
    then (a + b) | unique
  elif (a|type == "array") then a + [b] | unique
  elif (b|type == "array") then [a] + b | unique
  else [a, b] | unique
  end ;

有了任何这样的定义,我们现在可以继续回答:

# input is assumed to be an array of objects to be merged based on the filter f
def merge(f):
  def merge: reduce .[] as $object (null; merge(.; $object));
  group_by(f)
  | map(merge) ;

.Statement |= merge(.Sid)

【讨论】:

    【解决方案2】:

    在 OP 的许可下,我在这里发布了一个 alternative 解决方案,用于问题中的 JSON 操作,基于 walk-path unix 实用程序 jtc

    据我了解,需要合并包含SomeAPIUser 的“很好”记录(如果该术语也出现在记录之外,则可以轻松增强步行路径)。这是一个解决方案:

    $ <file.json jtc -w'<SomeAPIUser>[-1]' -pmi'<SomeAPIUser>1:[-1]' |
                 jtc -w'<SomeAPIUser>[-2][:]<q>Q:' -p |
                 jtc -x'<SomeAPIUser>[-2]<>i:<>f[1]<>F' -y' ' -y'[0]' -s
    

    此解决方案分为三个步骤:

    1。 jtc -w'&lt;SomeAPIUser&gt;[-1]' -pmi'&lt;SomeAPIUser&gt;1:[-1]'
    - 这里第一次出现SomeAPIUser 的记录与所有其他记录(递归地)合并(即使有多个)

    2。 jtc -w'&lt;SomeAPIUser&gt;[-2][:]&lt;q&gt;Q:' -p
    - 此步骤将删除步骤 1 中合并产生的所有重复记录。

    3。 jtc -x'&lt;SomeAPIUser&gt;[-2]&lt;&gt;i:&lt;&gt;f[1]&lt;&gt;F' -y' ' -y'[0]' -s
    - 在这最后一步中,所有具有单个 JSON 元素(由步骤 2 产生)的数组,例如:Effect": [ "Allow" ],都被转换为非数组记录,例如 Effect": "Allow"

    更新:

    使用最新版本的jtc,此解决方案提供了更强大的行为:

    $ <file.json jtc -w'[Sid]:<SomeAPIUser>[-1]' -pmi'[Sid]:<SomeAPIUser>1:[-1]' |
                 jtc -w'[Sid]:<>i>SomeAPIUser<[-2]<>i:><Q:' -p |
                 jtc -x'[Sid]:<>i>SomeAPIUser<[-2]<>i:<>f[1]<>F' -y' ' -y'[0]' -s
    

    - 它将SomeAPIUser 的搜索仅限于"Sid" 标签(因此当SomeAPIUser 可能与其他标签发生冲突时,它可以抵抗大小写);另外,当源 JSON 中只有一个(或没有)包含 "Sid":"SomeAPIUser" 的记录时,它也可以正常工作

    PS。我是用于 JSON 操作的 unix jtc 工具的开发人员。

    【讨论】: