jq - 在 JSON 文件的任何级别搜索字符串值并获取父键

jq - search a string value at any level of a json file and get the parent keys

提问人:mackermann 提问时间:10/27/2023 更新时间:11/8/2023 访问量:63

问:

我想在JSON文件中找到JSON文件的任何级别的特定值(而不用担心键)(我事先不知道确切的树)并显示父键。

我已经提取了这个样本:

cat /tmp/test.json | jq
{
  "totalCount": 2,
  "pageSize": 1000,
  "auditLogs": [
    {
      "logId": "169807591200060002",
      "eventType": "CREATE",
      "category": "CONFIG",
      "entityId": "HTTP_CHECK-12121212121212",
      "timestamp": 1698075912003,
      "success": true,
      "patch": [
        {
          "op": "replace",
          "path": "/",
          "value": {
            "steps": [
              {
                "id": {
                  "type": "HTTP_CHECK_STEP"
                },
                "requestType": "OAUTH2",
                "destinationUrl": "https://www.mywebsite.com",
                "httpMethod": "POST",
                "acceptAnyCertificate": true,
                "followRedirects": true,
                "displayName": "My Website",
                "userAgent": "",
                "httpCheckHeaders": [
                  {
                    "name": "Content-Type",
                    "value": "application/x-www-form-urlencoded"
                  }
                ],
                "stepPerformanceThreshold": 0,
                "requestBody": null,
                "constraints": [
                  {
                    "constraintType": "HttpStatusesList",
                    "passIfFound": false,
                    "pattern": ">=400"
                  }
                ],
                "preScript": "",
                "postScript": "",
                "attributes": {
                  "oAuth2RequestId": "1",
                  "oAuth2BodyInputType": "RAW",
                  "oAuth2addAuthDataTo": "REQUEST_BODY"
                },
                "postExecutionScriptVariables": [
                  "{bearerToken-1}"
                ],
                "preExecutionScriptVariables": [],
                "certificateId": "",
                "basicAuthId": "",
                "certStoreId": 0,
                "basicAuthStoreId": 0,
                "authenticationConfig": {
                  "type": "BASIC_AUTHENTICATION",
                  "realmName": null,
                  "kdcIp": null,
                  "credentialId": "CREDENTIALS_VAULT-1212121212121"
                },
                "executionProperties": {},
                "shouldNotPersistSensitiveData": true
              }
            ],
            "publicLocationIds": [
              124
            ],
            "userModificationTimestamp": 1698075911987,
            "customProperties": [],
            "version": 6
          },
          "oldValue": null
        }
      ]
    },
    {
      "logId": "169807591200060001",
      "eventType": "CREATE",
      "category": "CONFIG",
      "entityId": "HTTP_CHECK-12121212121212",
      "environmentId": "b416bf43-a9d2-4123-aa70-e36ff39c0ad9",
      "timestamp": 1698075911986,
      "success": true,
      "patch": [
        {
          "op": "replace",
          "path": "/",
          "value": {
            "frequency (Frequency)": 0,
            "locations": [
              {
                "location (Location)": "SYNTHETIC_LOCATION-000000000000007C"
              }
            ]
          },
          "oldValue": null
        }
      ]
    }
  ]
}

我能够使用此递归命令找到字符串“CREDENTIALS_VAULT-1212121212121”:

cat /tmp/test.json | jq '.auditLogs[] | .. | .credentialId? | select(. == "CREDENTIALS_VAULT-1212121212121")'                       
"CREDENTIALS_VAULT-1212121212121"

但我还想在第一级获取父键“logId”。我尝试使用变量($parent),但不幸的是,我得到了几个结果,而不仅仅是一个:

cat /tmp/test.json | jq '.auditLogs[] as $parent | .. | .credentialId? | select(. == "CREDENTIALS_VAULT-1212121212121") | $parent | {"logId":.logId}'
{
  "logId": "169807591200060002"
}
{
  "logId": "169807591200060001"
}

有人对这种需求有想法吗?

JSON 嵌套 JQ

评论

2赞 Paolo 10/27/2023
你能澄清一下所需的输出是什么吗?

答:

1赞 pmf 10/27/2023 #1

您可以使用查找具有匹配值的路径,从中提取前两项(在本例中),用于检索该对象,然后从那里提取:paths["auditLogs",0]getpath.logId

jq -r 'getpath(paths(. == "CREDENTIALS_VAULT-1212121212121")[:2]).logId'
169807591200060002

演示

如果希望对象仅包含字段,请改用:logId{logId}

jq 'getpath(paths(. == "CREDENTIALS_VAULT-1212121212121")[:2]) | {logId}'
{
  "logId": "169807591200060002"
}

演示

还可以考虑通过环绕筛选器来将结果收集到数组中。或者使用该选项从命令行导入搜索键等。[…]--arg

0赞 knittl 10/27/2023 #2

您可以使用 IN(或):any(stream; condition)

.auditLogs[]
| select(IN(.. | .credentialId?; "CREDENTIALS_VAULT-1212121212121"))
| .logId

或者,根据您的输出格式要求:

.auditLogs[]
| select(IN(.. | .credentialId?; "CREDENTIALS_VAULT-1212121212121"))
| { logId }
1赞 peak 10/28/2023 #3

...不用担心钥匙

...我事先不知道确切的树

这是一种满足这两个要求的简单方法(我认为比使用和更快):pathsgetpath

.. | objects
| select(.logId)
| .logId as $id
| .. | objects
| select(.[] == "CREDENTIALS_VAULT-1212121212121")
| $id

由于该字符串可能会出现多次,因此您可能需要考虑变体,例如将上面的倒数第二行替换为:

| select(first(.[] == "CREDENTIALS_VAULT-1212121212121" // empty))
0赞 mackermann 11/8/2023 #4

非常感谢您的回答。我最终选择使用 getpath() 函数使用第一个解决方案。使用此解决方案,我们不关心密钥的名称。事实上,在我的情况下,搜索模式(“CREDENTIALS_VAULT-1212121212121”)不是必需的,键“credentialId”的值。这可能是另一个关键:

$ grep "CREDENTIALS_VAULT-1212121212121" auditlogs2.json
                  "credentialId": "CREDENTIALS_VAULT-1212121212121"
                  "myotherkey": "CREDENTIALS_VAULT-1212121212121"

$ cat auditlogs2.json | jq -r 'getpath(paths(. == "CREDENTIALS_VAULT-1212121212121")[:2]) | .logId'
169807591200060002
169807591200060001

两个条目都已找到。这在我的情况下更合适;) 但再次感谢每一个人。