JQ выбрать в массиве или объекте - PullRequest
1 голос
/ 26 сентября 2019

С учетом этих трех документов ...

Один с объектом Statement:

{
    "PolicyVersion": {
        "CreateDate": "2017-07-13T18:59:21Z", 
        "VersionId": "v2", 
        "Document": {
            "Version": "2012-10-17", 
            "Statement": {
                "Action": "*", 
                "Resource": "*", 
                "Effect": "Allow"
            }
        }, 
        "IsDefaultVersion": true
    }
}

... и один с массивом Statement:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

... и один с оператором, вложенным в полный документ:

{
    "PolicyVersion": {
        "CreateDate": "2017-07-13T18:59:21Z", 
        "VersionId": "v2", 
        "Document": {
            "Version": "2012-10-17", 
            "Statement": [
                {
                    "Action": "*", 
                    "Resource": "*", 
                    "Effect": "Allow"
                }
            ]
        }, 
        "IsDefaultVersion": true
    }
}

Будет ли возможно иметь одну команду для выбора, когда в операторе существуют .Action == '*' и .Resource == '*', будь томассив, объект и где он вложен?

Например, я сканирую тысячи документов, единственное отличие которых в том, что Statement может быть объектом или массивом.

Конечночто-то вроде: jq '.PolicyVersion.Document.Statement[] | select((.Action == "*") and .Resource == "*") работает для массива, а jq '.PolicyVersion.Document.Statement | select((.Action == "*") and .Resource == "*") работает, когда это не массив, но я бы хотел добиться этого одной командой.

Я пробовал несколько вещей вокруг selectнапример:

jq '.PolicyVersion.Document | select((.Statement[] | select((.Action == "*") and .Resource == "*") or select((.Statement[] | select((.Action == "*") and .Resource == "*")'

, который ничего не возвращает, и:

jq '.PolicyVersion.Document | select((.Statement[] | select((.Action == "*") and .Resource == "*") or select((.Statement | select((.Action == "*") and .Resource == "*")'

, который возвращает ошибку цитирования оболочки Unix.

Ответы [ 3 ]

2 голосов
/ 26 сентября 2019

Другой подход, который весьма специфичен для данного конкретного случая:

.PolicyVersion.Document.Statement
| ..
| select(type == "object" and .Action == "*" and .Resource == "*")
2 голосов
/ 26 сентября 2019

Следующее может быть более общим, чем вам нужно, или может быть слишком общим, поэтому не стесняйтесь приправить по вкусу:

..
| objects
| select(.Statement)
| .Statement
| if type == "array" then .[] else . end
| select(.Action == "*" and .Resource == "*")
1 голос
/ 27 сентября 2019

С разрешения ОП, я покажу здесь, как выполнить тот же JSON-запрос, используя walk-path утилита unix jtc.Однако вместо объяснения walk-path я покажу, как его построить.

Мое понимание вопроса: проверяйте документы JSON, чтобы, если в документе есть записьс меткой Statement, которая содержит элементы "Action": "*" и "Resource": "*", затем распечатайте всю запись, содержащую эти элементы, в противном случае не делайте (т.е. оставляйте пустой вывод)

1.Начнем с нахождения записи JSON с рекурсивной меткой Statement (запись для рекурсивного поиска <..>, суффикс l указывает на поиск только меток ), поэтому она может находиться в любом местеДокумент JSON (я буду использовать первый пример JSON):

bash $ <file.json jtc -w'<Statement>l'
[
   {
      "Action": "*",
      "Effect": "Allow",
      "Resource": "*"
   }
]
bash $ 

2.Как только Statement запись найдена, нам нужно посмотреть, есть ли запись "*" где-то в найденной записи , прикрепленная к метке "Action" (снова используйте рекурсивный поиск, ноограничить поиск только записями с меткой Action - он же поиск по области ):

bash $ <file.json jtc -w'<Statement>l[Action]:<*>'
"*"
bash $ 

3.Теперь нам нужно посмотреть, есть ли брат ("Resource": "*") в найденной записи.Для этого давайте перейдем на один уровень вверх в дереве JSON (он же адрес родительского элемента):

bash $ <file.json jtc -w'<Statement>l[Action]:<*>[-1]'
{
   "Action": "*",
   "Effect": "Allow",
   "Resource": "*"
}
bash $

, а затем используем нерекурсивный поиск по области ( нерекурсивный обозначение поиска >..<):

bash $ <file.json jtc -w'<Statement>l[Action]:<*>[-1][Resource]:>*<'
"*"
bash $ 

4.Наконец, чтобы выбрать запись, давайте снова обратимся к родителю последней найденной / пройденной записи:

bash $ <file.json jtc -w'<Statement>l[Action]:<*>[-1][Resource]:>*<[-1]'
{
   "Action": "*",
   "Effect": "Allow",
   "Resource": "*"
}
bash $ 

Приведенный выше walk-path облегчит требуемый JSON-запрос для всех документов JSON (является ли Statement вложенным или нет, или записи Action и Resource (глубоко) зачислены в массив или нет - но они должны оставаться братьями и сестрами).
Если какая-либо из лексем ходьбы не удалась, тогда всявывод будет пустым:

bash $ <file.json jtc -w'<Statement>l[Action]:<blah>[-1][Resource]:>*<[-1]' 
bash $ 

Наконец, jtc быстрее при чтении из файла (вместо stdin), из соображений производительности лучше использовать его, передавая файл в качестве аргумента:

jtc -w'<Statement>l[Action]:<*>[-1][Resource]:>*<[-1]' file.json 

PS> Я создатель jtc Unix-утилиты для обработки JSON

...