JQ:如何在多级嵌套 JSON 中仅选择某些元素

JQ: How to select only some elements in multi-level nested JSON

提问人:John 提问时间:7/13/2023 更新时间:7/13/2023 访问量:94

问:

这有点长,但我不知如何将多个 jq 查询粘合在一起以过滤掉多级结构中的嵌套对象。

我从看起来像这样的JSON开始。

{
  "image": "test",
  "duration": 40,
  "image_size": 2758644359,
  "os": "rhel",
  "version": "9.2",
  "resources": [
    {
      "resource": {
        "format": "rpm",
        "name": "binutils",
        "version": "2.35.2-37.el9",
        "src_version": "2.35.2-37.el9"
      },
      "installed": true,
      "checks": [
        {
          "name": "check1",
          "description": "first check",
          "severity": "moderate",
          "depth": 5.5
        },
        {
          "description": "second check",
          "severity": "moderate",
          "depth": 4.7
        },
        {
          "name": "check3",
          "description": "third check",
          "severity": "low",
          "depth": 4.2
        }
      ]
    },
    {
      "resource": {
        "format": "rpm",
        "name": "dbus",
        "version": "1:1.12.20-7.el9_1",
        "src_version": "1:1.12.20-7.el9_1"
      },
      "installed": true,
      "checks": [
        {
          "description": "second check",
          "severity": "moderate",
          "depth": 6.2
        }
      ]
    },
    {
      "resource": {
        "format": "rpm",
        "name": "dmidecode",
        "version": "1:3.3-7.el9",
        "src_version": "1:3.3-7.el9"
      },
      "installed": true,
      "checks": [
        {
          "name": "check1",
          "description": "first check",
          "severity": "moderate",
          "depth": 5.5
        },
        {
          "description": "second check",
          "severity": "moderate",
          "depth": 7.1
        }
      ]
    },
    {
      "resource": {
        "format": "rpm",
        "name": "gcc",
        "version": "11.3.1-4.3.el9",
        "src_version": "11.3.1-4.3.el9"
      },
      "installed": true,
      "checks": [
      ]
    }
  ],
  "image_assurance_results": {},
  "check_summary": {
    "total": 6,
    "low": 1,
    "moderate": 5
  },
  "initiating_user": "test_user"
}

我想保留整体结构,并从顶层以及数组中的两个嵌套级别中选取选定的元素。这是我想要的结果。

{
  "image": "test",
  "os": "rhel",
  "resources": [
    {
      "resource": {
        "name": "binutils",
        "version": "2.35.2-37.el9"
      },
      "checks": [
        {
          "name": "check1",
          "description": "first check"
        },
        {
          "description": "second check"
        },
        {
          "name": "check3",
          "description": "third check"
        }
      ]
    },
    {
      "resource": {
        "name": "dbus",
        "version": "1:1.12.20-7.el9_1"
      },
      "checks": [
        {
          "description": "second check"
        }
      ]
    },
    {
      "resource": {
        "name": "dmidecode",
        "version": "1:3.3-7.el9"
      },
      "checks": [
        {
          "name": "check1",
          "description": "first check"
        },
        {
          "description": "second check"
        }
      ]
    },
    {
      "resource": {
        "name": "gcc",
        "version": "11.3.1-4.3.el9"
      },
      "checks": [
      ]
    }
  ],
  "check_summary": {
    "total": 6,
    "low": 1,
    "moderate": 5
  }
}

通过此查询,我可以消除不需要的顶级元素,保留资源下的完整元素集: 并得到这个结果jq '{image, os, resources, summary}'

{
  "image": "test",
  "os": "rhel",
  "resources": [
    {
      "resource": {
        "format": "rpm",
        "name": "binutils",
        "version": "2.35.2-37.el9",
        "src_version": "2.35.2-37.el9"
      },
      "installed": true,
      "checks": [
        {
          "name": "check1",
          "description": "first check",
          "severity": "moderate",
          "depth": 5.5
        },
        {
          "description": "second check",
          "severity": "moderate",
          "depth": 4.7
        },
        {
          "name": "check3",
          "description": "third check",
          "severity": "low",
          "depth": 4.2
        }
      ]
    },
    {
      "resource": {
        "format": "rpm",
        "name": "dbus",
        "version": "1:1.12.20-7.el9_1",
        "src_version": "1:1.12.20-7.el9_1"
      },
      "installed": true,
      "checks": [
        {
          "description": "second check",
          "severity": "moderate",
          "depth": 6.2
        }
      ]
    },
    {
      "resource": {
        "format": "rpm",
        "name": "dmidecode",
        "version": "1:3.3-7.el9",
        "src_version": "1:3.3-7.el9"
      },
      "installed": true,
      "checks": [
        {
          "name": "check1",
          "description": "first check",
          "severity": "moderate",
          "depth": 5.5
        },
        {
          "description": "second check",
          "severity": "moderate",
          "depth": 7.1
        }
      ]
    },
    {
      "resource": {
        "format": "rpm",
        "name": "gcc",
        "version": "11.3.1-4.3.el9",
        "src_version": "11.3.1-4.3.el9"
      },
      "installed": true,
      "checks": []
    }
  ],
  "summary": null
}

通过这个查询,我可以过滤掉嵌套的“检查”对象中不需要的元素jq '.resources[].checks[] | with_entries(select(.key | in({"name":1, "description":1})))'

{
  "name": "check1",
  "description": "first check"
}
{
  "description": "second check"
}
{
  "name": "check3",
  "description": "third check"
}
{
  "description": "second check"
}
{
  "name": "check1",
  "description": "first check"
}
{
  "description": "second check"
}

但是我无法弄清楚如何将这两个查询放在一起以获得所需的最终结果,并再添加一个操作以消除资源下的一些顶级元素。如何将with_entries过滤器应用于顶级的初始向下选择?

json 对象 选择 嵌套的 jq

评论


答:

0赞 pmf 7/13/2023 #1

使用而不是在不丢失整体结构的情况下进行更新。此外,使用(而不是 )来测试给定集合中是否包含值。总而言之:|=|INìn

{image, os, resources, summary}
| .resources[].resource |= with_entries(select(IN(.key; "name", "version")))
| .resources[].checks[] |= with_entries(select(IN(.key; "name", "description")))
{
  "image": "test",
  "os": "rhel",
  "resources": [
    {
      "resource": {
        "name": "binutils",
        "version": "2.35.2-37.el9"
      },
      "installed": true,
      "checks": [
        {
          "name": "check1",
          "description": "first check"
        },
        {
          "description": "second check"
        },
        {
          "name": "check3",
          "description": "third check"
        }
      ]
    },
    {
      "resource": {
        "name": "dbus",
        "version": "1:1.12.20-7.el9_1"
      },
      "installed": true,
      "checks": [
        {
          "description": "second check"
        }
      ]
    },
    {
      "resource": {
        "name": "dmidecode",
        "version": "1:3.3-7.el9"
      },
      "installed": true,
      "checks": [
        {
          "name": "check1",
          "description": "first check"
        },
        {
          "description": "second check"
        }
      ]
    },
    {
      "resource": {
        "name": "gcc",
        "version": "11.3.1-4.3.el9"
      },
      "installed": true,
      "checks": []
    }
  ],
  "summary": null
}

演示

现在,您可以进一步从循环中分解出来以防止双重迭代,并且显然,您还想在其中过滤(以删除):.resources[]{resource, checks}.installed

{image, os, resources, summary} | .resources[] |= (
  {resource, checks}
  | .resource |= with_entries(select(IN(.key; "name", "version")))
  | .checks[] |= with_entries(select(IN(.key; "name", "description")))
)
{
  "image": "test",
  "os": "rhel",
  "resources": [
    {
      "resource": {
        "name": "binutils",
        "version": "2.35.2-37.el9"
      },
      "checks": [
        {
          "name": "check1",
          "description": "first check"
        },
        {
          "description": "second check"
        },
        {
          "name": "check3",
          "description": "third check"
        }
      ]
    },
    {
      "resource": {
        "name": "dbus",
        "version": "1:1.12.20-7.el9_1"
      },
      "checks": [
        {
          "description": "second check"
        }
      ]
    },
    {
      "resource": {
        "name": "dmidecode",
        "version": "1:3.3-7.el9"
      },
      "checks": [
        {
          "name": "check1",
          "description": "first check"
        },
        {
          "description": "second check"
        }
      ]
    },
    {
      "resource": {
        "name": "gcc",
        "version": "11.3.1-4.3.el9"
      },
      "checks": []
    }
  ],
  "summary": null
}

演示


或者(而不是更新 ),使用建设性的方法,并直接设置您想要包含的所有内容(使用 on 数组):|=map

{
  image, os, "resources": .resources | map({
    "resource": .resource | with_entries(select(IN(.key; "name", "version"))),
    "checks": .checks | map(with_entries(select(IN(.key; "name", "description"))))
  }), summary
}

演示

评论

0赞 John 7/13/2023
超级乐于助人,谢谢@pmf
0赞 Jeff Mercado 7/13/2023 #2

或者,您可以通过选择要保留的属性来构造对象。然后在每个级别向下过滤。

{image, os, resources, check_summary}
  | .resources[] |= ({resource, checks}
    | .resource |= {name, version}
    | .checks[] |= {name, description}
  )

或者更直接地说,在一开始就构造出属性的结果。

{
    image,
    os,
    resources: .resources | map({
        resource: .resource | {name, version},
        checks: .checks | map({name, description})
    }),
    check_summary
}

但是,这些方法确实有缺点,它将保留不存在的属性并将它们设置为 。最后可以通过删除 .nullnull

| del(.. | select(. == null))