#json #select #jq
#json #выберите #jq
Вопрос:
Мой файл JSON имеет следующую структуру
[
{
"Header": {
"Tenant": "T1"
},
"Body": [
{
"agentTechnologyType": "JAVA",
"entityId": "SERVICE-EDA448F07EDBDAA6",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
},
{
"agentTechnologyType": "TOMCAT",
"entityId": "SERVICE-728B1FF49D132C89",
"serviceType": "WebRequest",
"webServerName": "www.example.com:80"
},
{
"agentTechnologyType": "JAVA",
"entityId": "SERVICE-42A0FB8666B36EB4",
"serviceType": "Process"
},
{
"agentTechnologyType": "APACHE",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
}
]
}
]
Мне нужно применить несколько фильтров и оставить в .Body[]
массиве объекты в соответствии с разными критериями:
- Выберите только те объекты в
.Body[]
массиве, у которых нет.webServerName
ключа — это условие должно оставить только объект # 2 внутри.Body[]
массива - Выберите только эти объекты в
.Body[]
массиве, где.webServerName
значение равноwww.example.com:443
— это должно оставить объекты # 0,3. - Выберите все объекты, у которых есть
.webServerName
ключ, поэтому отрицание условия # 1 — это должно оставить все объекты, кроме # 2.
Для # 1 приведенный ниже фильтр выполняет эту работу
[.[].Header as $x | (.[].Body[] | select( (.webServerName == null ))) as $y | {Tenant: $x, Body: [$y]} ]
Это дает следующий результат:
[
{
"Tenant": {
"Tenant": "T1"
},
"Body": [
{
"agentTechnologyType": "JAVA",
"entityId": "SERVICE-42A0FB8666B36EB4",
"serviceType": "Process"
}
]
}
]
Но когда я попытался изменить фильтр для условия # 2
[.[].Header as $x | (.[].Body[] | select( (.webServerName == "www.example.com:443" ))) as $y | {Tenant: $x, Body: [$y]} ]
Это дало мне следующий результат — две записи вместо одной.
[
{
"Tenant": {
"Tenant": "T1"
},
"Body": [
{
"agentTechnologyType": "JAVA",
"entityId": "SERVICE-EDA448F07EDBDAA6",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
}
]
},
{
"Tenant": {
"Tenant": "T1"
},
"Body": [
{
"agentTechnologyType": "APACHE",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
}
]
}
]
В то время как ожидаемый результат должен выглядеть как для случая # 2:
[
{
"Tenant": {
"Tenant": "T1"
},
"Body": [
{
"agentTechnologyType": "JAVA",
"entityId": "SERVICE-EDA448F07EDBDAA6",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
},
{
"agentTechnologyType": "APACHE",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
}
]
}
]
И следующее для случая # 3:
[
{
"Header": {
"Tenant": "T1"
},
"Body": [
{
"agentTechnologyType": "JAVA",
"entityId": "SERVICE-EDA448F07EDBDAA6",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
},
{
"agentTechnologyType": "TOMCAT",
"entityId": "SERVICE-728B1FF49D132C89",
"serviceType": "WebRequest",
"webServerName": "www.example.com:80"
},
{
"agentTechnologyType": "APACHE",
"serviceType": "WebRequest",
"webServerName": "www.example.com:443"
}
]
}
]
Я пытаюсь выяснить, где я допустил ошибку — в условии выбора или при формировании нового объекта в конце.
Ответ №1:
Судя по вашим требованиям, кажется, вам нужно выражение для выбора объектов в .Body[]
массиве, где .webServerName
есть ключ www.example.com:443
или ключ в принципе существует
.[].Body |= map(select(.webServerName == "www.example.com:443" or has("webServerName") ))
Обе ваши попытки, хотя и действительны, действительно запутаны. Стандартный шаблон для выборочной фильтрации объектов внутри массива — использовать |=
оператор с правильным select
выражением.
| {Tenant: $x, Body: [$y]}
Неверно, потому что оно применяется к каждому из 2 объектов, которые вы отфильтровали в предыдущем выражении. Таким образом, для каждого результата создано Tenant
поле. Поскольку у вас нет изменений в именах ключей (тот же ключ Tenant
), на самом деле нет смысла заново создавать новые объекты {..}
.
Комментарии:
1. Спасибо. Мой вопрос — почему
.webServerName != "www.example.com:443"
выдает вывод, который включает объект, у которого нет ключаwebServerName
внутри? Я понимаю, что могу включить дополнительный фильтрnull
, но хочу понять механизм.