1
votes

sélectionner dans un tableau ou un objet

Compte tenu de ces trois documents ...

Un avec un objet Statement:

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

... et un avec un tableau Statement:

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

... et un avec une déclaration imbriquée dans le document complet:

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

Serait-il possible d'avoir une seule commande pour sélectionner quand .Action == '*' et .Resource == '*' existent dans la déclaration, qu'il s'agisse d'un tableau, d'un objet et où il est imbriqué? p >

Par exemple, je scanne des milliers de documents où la seule différence est que la déclaration peut être un objet ou un tableau.

Bien sûr, quelque chose comme: jq '.PolicyVersion. Document.Statement [] | select ((. Action == "*") et .Resource == "*") fonctionne pour un tableau et jq '.PolicyVersion.Document.Statement | select ((. Action == "*") et .Resource == "*") fonctionne quand ce n'est pas un tableau mais que j'aimerais le réaliser en une seule commande.

J'ai essayé quelques trucs autour de select comme:

jq '.PolicyVersion.Document | select ((. Statement [] | select ((. Action == "*") et .Resource == "*") ou sélectionnez ((. Statement [] | select ((. Action == "*") et. Resource == "*") '

qui ne renvoie rien, et:

jq' .PolicyVersion.Document | select ((. Statement [] | sélectionnez ((. Action == "*") et .Resource == "*") ou sélectionnez ((. Statement | select ((. Action == "*") et .Resource == "*") ' code>

qui renvoie une erreur de citation du shell Unix.


4 commentaires

pour votre considération, la même requête JSON pourrait être réalisée en utilisant un alternative unix walk-path utilitaire jtc : l [Action]: <*> [- 1] [Ressource]: <*> [^ 0]' . Si vous le souhaitez, je peux élaborer dans une réponse séparée sur l'utilisation de l'outil (PS. Je suis un développeur de l'outil).


Merci d'avoir diffusé les connaissances, @Dmitry :) Toujours apprécier les alternatives et apprendre de différentes manières.


btw, dans l'exemple ci-dessus, tout le document est sélectionné (je pourrais mal comprendre la question), dans la réponse ci-dessous, je vais expliquer comment sélectionner uniquement l'enregistrement dans Statement contenant Action et Ressource si les deux sont "*" . Faites-moi savoir si la demande avait une signification différente.


J'ai également trouvé une fonction lambda utilisant jmespath qui y parvient en utilisant ce qui suit: if jmespath.search ('PolicyVersion.Document.Statement [? Effect == \' Allow \ '&& contient (Ressource , \ '* \') && contains (Action, \ '* \')] ', policy_version): pensait que je partagerais ici comme une autre approche / solution possible.


3 Réponses :


2
votes

Ce qui suit peut être plus générique que nécessaire, ou peut-être trop générique, alors n'hésitez pas à assaisonner à votre goût:

..
| objects
| select(.Statement)
| .Statement
| if type == "array" then .[] else . end
| select(.Action == "*" and .Resource == "*")


1 commentaires

Fantastique, merci! Fonctionne pour tous les cas d'utilisation auxquels je peux penser / j'ai vu jusqu'à présent (les trois que j'ai inclus dans mon OP final). Si vous le pouviez, cela vous dérangerait-il de décrire comment cela fonctionne? Merci encore :)



2
votes

Une autre approche assez spécifique à ce cas particulier:

.PolicyVersion.Document.Statement
| ..
| select(type == "object" and .Action == "*" and .Resource == "*")


2 commentaires

Cela fonctionne, merci. J'ai mis à jour ma question telle qu'elle a été publiée avant que j'aie fini d'ajouter mes exemples de cas d'utilisation. Peut-être une autre question, mais je cherche également à faire fonctionner cela si la déclaration est à la racine du document et non imbriquée également.


@TryTryAgain Reportez-vous à la réponse de Peak pour cela



2
votes

Avec l'autorisation de l'OP, permettez-moi de montrer ici comment réaliser la même requête JSON en utilisant un utilitaire unix walk-path jtc . Cependant, au lieu d'expliquer le chemin de marche , je vais montrer comment le construire.

Ma compréhension de la question: valider les documents JSON afin que s'il y a n'importe où dans le document une entrée avec le label Statement qui contient les éléments "Action": "*" et "Resource": "*" , puis imprimez l'intégralité de l'enregistrement contenant ces éléments , sinon ne le faites pas (c'est-à-dire laissez une sortie vide)

1. Commençons par rechercher une entrée JSON avec l'étiquette Statement de manière récursive (une notation pour une recherche récursive est <..> , le suffixe l indique search labels uniquement), afin qu'il puisse être n'importe où dans le document JSON (j'utiliserai le premier exemple JSON):

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

2. Une fois l'enregistrement Statement trouvé, nous devons voir s'il y a un enregistrement "*" quelque part dans l'entrée trouvée joint strong > au libellé "Action" (utilisez à nouveau une recherche récursive mais limitez la recherche uniquement aux entrées avec le libellé Action - aka recherche étendue ):

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

3. Nous devons maintenant voir s'il y a un frère ( "Resource": "*" ) à l'entrée trouvée. Pour cela, montons d'un niveau dans l'arborescence JSON (c'est-à-dire adressons à un parent):

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

puis utilisons une recherche à portée non récursive une notation de recherche non récursive est >..):

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

4. Enfin, pour sélectionner l'enregistrement, adressons à nouveau un parent de la dernière entrée trouvée / parcourue:

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

Le chemin de marche ci-dessus facilitera les Requête JSON pour tous les documents JSON (si Statement est imbriqué ou non, ou si les enregistrements Action et Resource sont (profondément) inscrits dans le tableau ou non - mais ils doivent rester frères et sœurs).
Si l'un des lexèmes de marche échoue, alors la sortie entière sera vide:

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

Enfin, jtc est fois plus rapide lors de la lecture d'un fichier (à la place de stdin ), pour des raisons de performances, il est préférable de l'utiliser en passant le fichier comme argument:

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

PS> Je suis le créateur du jtc utilitaire unix pour le traitement JSON


0 commentaires