Как я могу фильтровать по нескольким идентификаторам объектов, используя эквивалентные символы или чередование в JQ?
Резюме
У меня есть работающий фильтр JQ, который правильно анализирует три различных объекта имени и полезной нагрузки и массирует их в желаемый выходной формат. Проблема в том, что мне приходится явно выражать каждый путь к объекту, так как я не могу найти способ выразить изменение в идентификаторах объекта.
Мне бы хотелось, чтобы фильтр был более гибким, чтобы он мог отображать структуру данных из 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
Это мой текущий фильтр, который работает, но явно определяет каждый необязательный объект 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 } } ]
2 ответа
Хитрость заключается в том, чтобы определить вспомогательную функцию. Если, например, вы пишете:
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
иллюстрирующий (а) как избежать 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( . ) )
Эта вспомогательная функция, возможно, ближе к тому, что вы ищете:
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)
}