如何用jq筛选OpenAPI/Swagger JSON中标记为public的接口方法
解决OpenAPI规范中保留public方法的jq过滤问题
问题描述
我是jq新手,搞不定符合需求的过滤器,求助:我有一份OpenAPI 3.0(或Swagger 2.0)的规范文件,其中paths元素包含多个endpoint,每个endpoint下有get、post等方法。我需要一个jq过滤器,仅保留标记有"x-visibility": "public"的方法,移除其他方法。我尝试过以下命令:
.paths |= map(select(.[]."x-visibility" == "public" | not))
但显然无法正常工作。以下是简化后的OpenAPI 3.0规范示例:
{"openapi": "3.0.1","info": {"title": "API service","description": "Exposing useful methods","x-descriptionForPublicDocs": "This is a test description for public docs specifically","contact": {"name": "Message me","url": "https://message.me"},"version": "1.0"},"servers": [{"url": "http://localhost:9090","description": "Generated server url"}],"tags": [{"name": "AccountGroups"}],"x-tags": [{"name": "TestResourceLists"}],"paths": {"/crane/v1/resource_list/{id}": {"get": {"tags": ["ResourceLists"],"x-tags": "TestResourceLists","x-visibility": "public","summary": "Get Resource List by ID","description": "Returns the resource list that has the specified ID.","operationId": "getById","parameters": [{"name": "id","in": "path","description": "ResourceList Id","required": true,"schema": {"type": "string"}}],"responses": {"200": {"description": "Successful operation","content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/ResourceListModel"}}}},"400": {"description": "Bad Request","content": {"*/*": {"schema": {"type": "object"}}}},"403": {"description": "Forbidden","content": {"*/*": {"schema": {"type": "object"}}}},"500": {"description": "Internal Server Error","content": {"*/*": {"schema": {"type": "object"}}}},"4XX": {"description": "Client error","content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/SpringErrorResponse"}}}}}},"put": {"tags": ["ResourceLists"],"x-visibility": "internal","summary": "Update Resource List","description": "Updates the resource list that has the specified ID.","operationId": "update","parameters": [{"name": "id","in": "path","description": "ResourceList Id","required": true,"schema": {"type": "string"}}],"requestBody": {"description": "Model for Resource List","content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourceListModel"}},"examples": {"TAG": {"description": "Tag ResourceList","value": {"members": [{"string": "string"}],"name": "string","resourceListType": "TAG","description": "string"}},"RESOURCE_GROUP": {"description": "RESOURCE_GROUP ResourceList","value": {"description": "string","members": ["string","string"],"name": "string","resourceListType": "RESOURCE_GROUP"}},"COMPUTE_ACCESS_GROUP": {"description": "COMPUTE_ACCESS_GROUP ResourceList","value": {"description": "string","members": [{"appIDs": ["*"],"clusters": ["*"],"codeRepos": ["*"],"containers": ["*"],"functions": ["*"],"hosts": ["*"],"images": ["*"],"labels": ["*"],"namespaces": ["*"]}],"name": "string","resourceListType": "COMPUTE_ACCESS_GROUP"}}}},"required": true},"responses": {"200": {"description": "Successful operation","content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/ResourceListModel"}}}},"400": {"description": "Bad Request","content": {"*/*": {"schema": {"type": "object"}}}},"403": {"description": "Forbidden","content": {"*/*": {"schema": {"type": "object"}}}},"500": {"description": "Internal Server Error","content": {"*/*": {"schema": {"type": "object"}}}},"4XX": {"description": "Client error","content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/SpringErrorResponse"}}}}}},"delete": {"tags": ["ResourceLists"],"x-visibility": "internal","summary": "Delete Resource List","description": "Deletes the resource list that has the specified ID.","operationId": "delete","parameters": [{"name": "id","in": "path","description": "ResourceList Id","required": true,"schema": {"type": "string"}}],"responses": {"204": {"description": "Successful operation"},"400": {"description": "Bad Request","content": {"*/*": {"schema": {"type": "object"}}}},"403": {"description": "Forbidden","content": {"*/*": {"schema": {"type": "object"}}}},"500": {"description": "Internal Server Error","content": {"*/*": {"schema": {"type": "object"}}}},"4XX": {"description": "Client error","content": {"*/*": {"schema": {"$ref": "#/components/schemas/SpringErrorResponse"}}}}}}},"/crane/v1/account_group/{id}": {"get": {"tags": ["AccountGroups"],"x-visibility": "public","summary": "Get resource","description": "Returns something","operationId": "getById_1","parameters": [{"name": "id","in": "path","description": "Account Group ID","required": true,"schema": {"type": "string"}},{"name": "includeAccountInfo","in": "query","description": "Include Cloud Account details","required": false,"schema": {"type": "boolean"}}],"responses": {"200": {"description": "Successful operation","content": {"application/json": {"schema": {"$ref": "#/components/schemas/AccountGroupModel"}}}},"400": {"description": "Bad Request","content": {"*/*": {"schema": {"type": "object"}}}},"403": {"description": "Forbidden","content": {"*/*": {"schema": {"type": "object"}}}},"500": {"description": "Internal Server Error","content": {"*/*": {"schema": {"type": "object"}}}},"4XX": {"description": "Client error","content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/SpringErrorResponse"}}}}}},"put": {"tags": ["AccountGroups"],"x-visibility": "internal","summary": "Update resource","description": "Update information","operationId": "update_1","parameters": [{"name": "id","in": "path","description": "AccountGroup Id","required": true,"schema": {"type": "string"}}],"requestBody": {"content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/AccountGroupModel"}}}},"required": true},"responses": {"200": {"description": "Successful operation"},"400": {"description": "Bad Request","content": {"*/*": {"schema": {"type": "object"}}}},"403": {"description": "Forbidden","content": {"*/*": {"schema": {"type": "object"}}}},"500": {"description": "Internal Server Error","content": {"*/*": {"schema": {"type": "object"}}}},"4XX": {"description": "Client error","content": {"application/json; charset=UTF-8": {"schema": {"$ref": "#/components/schemas/SpringErrorResponse"}}}}}},"delete": {"tags": ["AccountGroups"],"x-visibility": "internal","summary": "Delete resource","description": "Deletes the resource","operationId": "delete_1","parameters": [{"name": "id","in": "path","description": "Delete resource","required": true,"schema": {"type": "string"}}],"responses": {"200": {"description": "Successful operation"},"400": {"description": "Bad Request","content": {"*/*": {"schema": {"type": "object"}}}},"403": {"description": "Forbidden","content": {"*/*": {"schema": {"type": "object"}}}},"500": {"description": "Internal Server Error","content": {"*/*": {"schema": {"type": "object"}}}},"4XX": {"description": "Client error","content": {"*/*": {"schema": {"$ref": "#/components/schemas/SpringErrorResponse"}}}}}}}}}
解决方案
问题出在哪
你之前的命令用了map,但paths是对象不是数组,map只适用于数组;另外逻辑也搞反了,你要保留x-visibility为public的方法,结果写的是排除它们。
正确的jq过滤器
.paths |= with_entries( .value |= with_entries( select(.value."x-visibility" == "public") ) )
代码解释
with_entries:专门用来处理对象的键值对,比map适配paths这种结构- 第一层
with_entries:遍历每个endpoint路径(比如/crane/v1/resource_list/{id}) - 第二层
with_entries:遍历每个endpoint下的HTTP方法(get/put/delete等) select(.value."x-visibility" == "public"):只保留方法里x-visibility等于public的条目
进阶优化:移除空endpoint
如果某个endpoint下的所有方法都被过滤掉了,你可能想把这个空的endpoint也删掉,用下面的命令:
.paths |= with_entries( .value |= with_entries( select(.value."x-visibility" == "public") ) | select(length > 0) )
内容的提问来源于stack exchange,提问作者3agpot
相关产品推荐
相关产品推荐

