首页 > 解决方案 > 需要过滤值包含特定子字符串的所有键/值对

问题描述

我尝试为包含值“;自版本”的所有“描述”键过滤 JSON 文件,以打印它们的路径和该值中包含的版本。我在 bash 脚本中完成所有这些工作。

我正在过滤的 JSON 来自 API,并且在不同路径上具有“描述”键。

这是服务器 JSON 的摘录

到目前为止,我使用 curl 从服务器获取 JSON 并将其通过管道传递给 jq 应用以下过滤器以获取包含其描述中的值的所有路径的列表:

curl $api | jq 'paths(objects and (.description|index("; Since version")))'

这将返回一个列表,其中包含指向包含搜索值的端点的路径,如下所示:

[
  "paths",
  "/v4/user/profileAttributes/{key}",
  "delete"
]
[
  "paths",
  "/v4/users/{user_id}/last_admin_rooms",
  "get"
]
[
  "definitions",
  "GeneralSettings",
  "properties",
  "s3TagsEnabled"
]

当适应我搜索的字符串而不是整个列表时,我发现此过滤器返回看似随机的“描述”键/值对的值。不幸的是,我不太了解整个过滤器,因为作者没有在他的帖子中解释它。适应时

curl -s -N $api | jq '. as $in
| reduce paths(type == "string" and test("; Since version")) as $path ({};
    ($in|getpath($path)) as $x
    | if ($path[-1]|type) == "string"
      then .[$path[-1]] = $x
      else .[$path[-2]|tostring] += [$x]
      end )'

返回

{
  "description": "Some_Text_We_Dont_Care_About; Since version 4.10.2 Some_More_Text_We_Dont_Care_About"
}

如果第二个过滤器将返回所有条目而不是(接缝随机的)条目,我将能够将两个过滤后的响应拼接在一起。我在这里发布了两个过滤器,因为也许有人知道如何以更清洁的方式进行操作。

理想情况下,最终结果将返回一个包含如下条目的列表:

{
  "path": "
    [
    "paths",
    "/v4/users/{user_id}/last_admin_rooms",
    "get"
    ]"
  ",
  "version": {
    "description": "Some_Text_We_Dont_Care_About; Since version 4.10.2 Some_More_Text_We_Dont_Care_About"
  }
}

这是输入内容的摘录/示例:

{
  "info": {4 items},
  "host": "some_hostname",
  "basePath": "/api",
  "tags": [19 items],
  "paths": {
    "/v4/config/info/defaults": {
      "get": {
        "tags": [
          "config"
        ],
        "summary": "Get default values",
        "description": "SomeText; Since version 4.6.0 SomeMoreText",
        "operationId": "getSystemDefaultsInfo",
        "produces": [
          "application/json;charset=UTF-8"
        ],
      }
    }
    "/v4/config/info/general": {
      "get": {
        "tags": [
          "config"
        ],
        "summary": "Get general values",
        "description": "SomeText; Since version 4.6.0 SomeMoreText",
        "operationId": "getSystemDefaultsInfo",
        "produces": [
          "application/json;charset=UTF-8"
        ],
      }
    }
  "definitions": {
    "GeneralSettings": {
      "type": "object",
      "properties": {
        "cryptoEnabled": {
          "type": "boolean",
          "description": "Activation status of encryption"
        },
        "s3TagsEnabled": {
          "type": "boolean",
          "description": "Defines if S3 tags are enabled; Since version 4.9.0 NEW"
        },
        "sharePasswordSmsEnabled": {
          "type": "boolean",
          "description": "Allow sending of share passwords via SMS"
        }
      }
    }
  }
}

标签: jsonjq

解决方案


让我为您提供一个替代解决方案 - 使用基于步行路径的 unix 实用程序jtc

根据您想要的输出,jtc查询将如下所示:

bash $ <file.json jtc -w'[description]:<.*; Since version.*>R: [-1] <act>k [-1] <path>k [-1] <pathname>k' -T'{ "path": [ {{pathname}}, {{path}}, {{act}}], "version": { "description": {{$0}}} }'
{
   "path": [
      "paths",
      "/v4/config/info/defaults",
      "get"
   ],
   "version": {
      "description": "SomeText; Since version 4.6.0 SomeMoreText"
   }
}
{
   "path": [
      "paths",
      "/v4/config/info/general",
      "get"
   ],
   "version": {
      "description": "SomeText; Since version 4.6.0 SomeMoreText"
   }
}
{
   "path": [
      "GeneralSettings",
      "properties",
      "s3TagsEnabled"
   ],
   "version": {
      "description": "Defines if S3 tags are enabled; Since version 4.9.0 NEW"
   }
}
bash $ 

鉴于您的 Json 是不规则的(步行description要求),但不确定是否需要查看最后一个对象。如果您不需要它,那么了解 JSON 的结构很容易增强查询以排除像最后一个那样的误报匹配。

编辑:说明:

-T选项提供了一个 Json 模板,如果 walk 成功,则在{{..}}每次 walk ( ) 结束时从命名空间中插入模板项(括在双花括号中)。-w

现在 walk-path ( -w) 是遍历源 JSON 的方式:jtc让 Json 自由地上下行走(使用下标[..])并执行递归搜索<..>。虽然有些项目是directives- 它们不进行任何搜索/匹配,而是应用某些操作(此符号中的单字母后缀<..>S定义它是搜索还是指令)

让我在这里分解一下(顺便说一句,链接中描述了所有词位):

  • [description]:<.*; Since version.*>R:- 对包含 的 JSON 字符串执行 RE 搜索; Since version,但由于 RE 拼写匹配整个此类字符串(请注意.*在 RE 表达式的开头和结尾处)。现在附加标签[description]:确保这种 RE 匹配只会发生在带有附加标签的那些字符串中(而不是任何其他字符串)。:在词位(又名量词)的末尾指示查找所有此类事件(搜索和与 Python 表示法兼容的下标中的量词)。
  • [-1]<act>k-[-1]寻址找到的条目的父节点(来自上一个遍历步骤),并<act>k记住命名空间中条目的标签act(用于以后的模板插值) - 搜索词素末尾的后缀定义它是搜索还是指令(在这种情况k是提取标签并在命名空间中记忆的指令)
  • 步行路径的重置是相同的:[-1]<path>k将从最后找到的条目(它又是第一步中找到的条目的父条目)寻址父级,并将在名称空间中记住其名称下的标签(键)path(名称是任意的jtc
  • [-1]<pathname>k'- 做几乎相同的事情,然后步行结束(好吧,对于给定的迭代 - 如果你记得第一个步行词素是迭代的 - 它找到每个出现)然后应用模板插值,从而产生打印项目。

您可以轻松地使用查询(删除每个词位或添加您的词素,看看步行是如何工作的) - 我编辑了词位路径,将每个词位与空格分开。

PS> 披露:我是jtc工具的创造者


推荐阅读