【问题标题】:How can I filter for multiple object identifiers using the equivalent of wildcards or alternation in jq?如何在 jq 中使用等效的通配符或交替来过滤多个对象标识符?
【发布时间】:2017-05-06 16:38:46
【问题描述】:

总结

我有一个有效的 jq 过滤器,它可以正确解析三个不同的 namepayload 对象,并将它们按摩成所需的输出格式。问题是我必须明确表达每个对象路径,因为我无法找到在对象标识符中表达交替的方法。

我希望过滤器更灵活,以便它可以显示从containers 到任何标题以Packages 开头的包管理器的数据结构。不过,我需要整个结构,而不仅仅是终端节点。

认为我需要的是用交替或通配符来表达我的对象标识符,例如:

  • .capabilities.*.payload?
  • .capabilities.([apk, dpkg, rpm]).payload?

我意识到以上都不是有效的 jq 语法,这就是问题的原因。我在下面包含了一个带有有效 JSON 的测试语料库,我当前的 jq 过滤器列在下面的部分中。

最小的 JSON 文件

这是我的测试语料库,保存为 minimal.json 在当前目录中。

{
  "containers": {
    "3dc76c82e566a116e5b64bc91a0b6220c71db7052f68317ebbe90521db55bf36": {
      "container_name": "/apache-46869",
      "capabilities": {
        "apk": {
          "title": "Packages (APK)"
        },
        "dpkg": {
          "title": "Packages (DPKG)",
          "payload": {
            "apt": "1.0.9.8.4",
            "libnghttp2-14": "1.18.1-1"
          }
        },
        "rpm": {
          "title": "Packages (RPM)"
        }
      }
    },
    "474047a1fe238e39fa1917aff0c93154624bbf159d321d49d5e685302589ab51": {
      "container_name": "/nginx-alpine-46869",
      "capabilities": {
        "apk": {
          "title": "Packages (APK)",
          "payload": {
            ".nginx-rundeps": "0",
            "apk-tools": "2.6.8-r2"
          }
        },
        "dpkg": {
          "title": "Packages (DPKG)"
        },
        "rpm": {
          "title": "Packages (RPM)"
        }
      }
    },
    "d7dcd90791240d78022941cf054a6b474f5329acd79aa15b58dc342f95a8ce33": {
      "container_name": "/apache-alpine-46869",
      "capabilities": {
        "apk": {
          "title": "Packages (APK)",
          "payload": {
            ".httpd-rundeps": "0",
            "apk-tools": "2.6.8-r2",
            "apr": "1.5.2-r1",
            "apr-util": "1.5.4-r2"
          }
        },
        "dpkg": {
          "title": "Packages (DPKG)"
        },
        "rpm": {
          "title": "Packages (RPM)"
        }
      }
    }
  }
}

显式 jq 过滤器

这是我当前的过滤器,它有效但明确定义了每个optional object indentifier-index

jq '
    [ .containers[] | { 
        name: .container_name, package_inventory: {
            apk: .capabilities.apk.payload?,
            dpkg: .capabilities.dpkg.payload?,
            rpm: .capabilities.rpm.payload?
        }   
    }]  
' minimal.json

预期输出

我当前的输出(如下所示)是正确的。目标不是修复输出,而是让过滤器更灵活。

[
  {
    "name": "/apache-46869",
    "package_inventory": {
      "apk": null,
      "dpkg": {
        "apt": "1.0.9.8.4",
        "libnghttp2-14": "1.18.1-1"
      },
      "rpm": null
    }
  },
  {
    "name": "/nginx-alpine-46869",
    "package_inventory": {
      "apk": {
        ".nginx-rundeps": "0",
        "apk-tools": "2.6.8-r2"
      },
      "dpkg": null,
      "rpm": null
    }
  },
  {
    "name": "/apache-alpine-46869",
    "package_inventory": {
      "apk": {
        ".httpd-rundeps": "0",
        "apk-tools": "2.6.8-r2",
        "apr": "1.5.2-r1",
        "apr-util": "1.5.4-r2"
      },
      "dpkg": null,
      "rpm": null
    }
  }
]

【问题讨论】:

    标签: json wildcard jq alternation


    【解决方案1】:

    诀窍是定义一个辅助函数。例如,如果你写:

    def payloads(keys): . as $in
      | reduce keys[] as $key ({}; .[$key] = ($in|.[$key].payload?) );
    

    那么你的查询变成:

    .containers[] | { 
        name: .container_name,
        package_inventory: (.capabilities | payloads( ["apk","dpkg","rpm"] ))
    }
    

    当然,其他变体也是可能的。例如,您可以将payloads 定义为arity-2 函数,从而传入“功能”。

    使用 JSON 对象指定键

    这里是payloads/1 的变体,说明 (a) 如何避免 reduce,以及 (b) 如何通过将 JSON 对象作为模板来指定键:

    def payloads_at(object):
      . as $in
      | object as $object
      | ({}
         | [($object|keys_unsorted[]) as $key
            | .[$key] = ($in|.[$key].payload?) ])
      | add;
    

    这可以这样调用:payloads_at( {apk, dpkg, rpm} ),或者如果您希望动态确定密钥:

    (.capabilities | payloads_at( . ) )
    

    【讨论】:

    • 这似乎可行,虽然我不完全确定我完全理解为什么。当我提前知道包类型时,它似乎可以解决交替的用例,但是也可以解决任意包类型吗?换句话说,是否可以让它更像通配符而不是选择器白名单?
    • 你可以使用keys(或keys_unsorted)来生成一个key数组;更好的是,使用 payload_at/1,您可以使用所需的键传递任何对象,例如.capabilties 的对象。查看更新的答案。
    【解决方案2】:

    这个辅助函数可能更接近您正在寻找的内容:

    def star(pre; template; post):
      pre as $object
      | ({} | [($object|template|keys_unsorted[]) as $key | .[$key] = ($object | .[$key] | post) ])
      | add;
    

    用法

    显式键名列表:

    star(.capabilities; {apk,dpkg,rpm}; .payload)
    

    .capabilities 的键:

    star(.capabilities; .; .payload)
    

    示例:

    .containers[] | { 
        name: .container_name,
        package_inventory: star(.capabilities; .; .payload)
    }
    

    【讨论】:

      猜你喜欢
      • 2012-09-01
      • 2016-03-02
      • 1970-01-01
      • 1970-01-01
      • 2015-04-16
      • 1970-01-01
      • 2017-04-03
      • 2023-01-28
      • 1970-01-01
      相关资源
      最近更新 更多