Journal JSON en ligne de commande : jq/pjy

Posté par . Licence CC by-sa.
Tags :
22
20
fév.
2018

Salut à toi, Nal,

Connais tu jq ?

C'est un outil en ligne de commande bien pratique quand on a des données en JSON et qu'on veut en extraire quelques infos. On lui passe un fichier JSON, une expression bien sentie dans un langage spécifique et concis, et le tour est joué.

Alors, je sais pas pour toi, Nal, mais je galère quand même pas mal chaque fois pour réussir à écrire l'expression qui va bien, car jq a son langage à lui, et il n'est pas des plus simples. Je trouve que ça devient assez compliqué dès qu'il s'agit de faire une boucle qui fait un peu plus que retourner un champ d'un dictionnaire.

Ça serait quand même plus pratique si la syntaxe était celle d'un langage que je maitrise déjà, par exemple Python.

Bon, à ce stade là de mon approche pas très discrète, tu dois te douter que je vais tenter de te vendre ma came. Alors allons-y : j'ai écrit pjy, un outil comme jq, mais qui utilise la syntaxe de Python. Si tu connais Python, tu sais (presque) déjà utiliser pjy.

pjy 'data["toto"] + 1' fichier.json

Comme pour jq, on passe d'abord l'expression à évaluer (en Python cette fois), et le fichier JSON. data représente le fichier JSON. Ici, le fichier JSON était un dictionnaire avec une clef toto, et j'affiche la valeur associée à cette clef, plus un. Simple.

Malgré sa syntaxe facétieuse, il faut bien avouer que la notation de jq x.y au lieu de x["y"] est pratique. Je l'ai donc emprunté (et ajouté un alias "d" pour "data") :

pjy 'd.toto + 1' fichier.json

À ce moment là, j'étais déjà content du résultat, débarrassé de jq, je pouvais enfin écrire une double boucle sans m'arracher les cheveux :

[[s[:i] for i in range(len(s))] for s in d.chaines]

Mais je me suis dit que jq avait quelques petits trucs sympas, que je pouvais également emprunter, de manière optionnelle bien entendue, pour qu'on puisse toujours revenir à une syntaxe Python, standard et lisible, qui est la raison d'être de pjy.

J'ai donc emprunté à jq la barre verticale pour réaliser la fonction map, on peut ainsi remplacer :

map(lambda x: x + 1, data["nombres"])

Par :

data["nombres"] | lambda x: x + 1

Si tu as tout suivi, tu noteras que l'on peut également écrire :

d.nombres | lambda x: x + 1

J'avais repéré depuis pas mal de temps le paquet placeholder, qui permet d'écrire des lambdas basiques de manière assez concise. Je l'ai ré-implémenté et rajouté la variable _ qui une fonction spéciale, au début la fonction identité, mais qui absorbe toutes les opérations pour les ré-appliquer plus tard. En reprenant l'exemple précédent, ça donne :

d.nombres | _ + 1

Il y a quelques petites fonctionnalités en plus (comme plusieurs fichiers en entrée), que tu pourras découvrir.
Si tu es curieux ou convaincu, je t'invite à tester : https://pypi.python.org/pypi/pjy

pip3 install pjy

C'est du Python 3, pas de Python 2, et pas de dépendances externes (pygments est optionnel, pour la coloration). C'est sous licence WTFPL.
À noter ce paquet qu'il peut être intéressant d'utiliser dans pjy : http://0101.github.io/pipetools/doc/

Que penses-tu de tout ça, Nal ?

  • # jq

    Posté par (page perso) . Évalué à 9.

    Pour info, S. Bortzmeyer a écrit un billet sympa sur jq http://www.bortzmeyer.org/jq.html . Pour le reste, c'est le classique problème de la poule et de l'oeuf, jq est disponible dans (quasi?) toutes les distributions même anciennes, il est déjà bien implanté, etc. D'où l'intérêt de montrer en quoi pjy fait mieux/plus. Par ailleurs pjy vise probablement plus dans un premier temps les développeurs python qui utilisent de toute façon déjà pip régulièrement pour ramener des paquets divers et variés.

    • [^] # Re: jq

      Posté par . Évalué à 3.

      Et bien j'avais raté ce post de Bortzmeyer ! Merci du lien, c'est très intéressant.
      ( Voilà encore une fois l'utilité d'une section "Brèves" où on pourrait poster des bookmarks… )

      • [^] # Re: jq

        Posté par (page perso) . Évalué à 3.

        ( Voilà encore une fois l'utilité d'une section "Brèves" où on pourrait poster des bookmarks… )

        oui, il y a une entrée : https://linuxfr.org/suivi/une-categorie-bookmark peu commentée :/
        pour créer une section de dépêches : proposer un nom, fournir une image en SVG préférentiellement, faire l'entrée de suivi… profit !

  • # D'autres alternatives

    Posté par (page perso) . Évalué à 8.

    À noter qu'il existe d'autres alternatives pour manipuler du JSON :

    • [^] # Re: D'autres alternatives

      Posté par . Évalué à 4.

      En dehors de sa syntaxe parfois difficile à mémoriser pour les expressions compliquées et de sa gestion des number, le plus gros reproche que j'avais à faire à jq ce sont ses performances. Il plafonne à quelques MB/s.

      As tu une idée si les autres outils sont un peu moins limités de ce côté là ?

    • [^] # Re: D'autres alternatives

      Posté par . Évalué à 2.

      Ils ont globalement tous une syntaxe particulière spécifique à apprendre et qui atteindra probablement vite ses limites face à des cas un tant soi peu complexes.

  • # Dans le même registre jsawk

    Posté par . Évalué à 5.

    J’en ai utilisé un autre dans le genre dans le passé : https://github.com/micha/jsawk . Comme awk balance du texte en sortie, ça peut balancer du json en sortie. On peut extraire des données pour les sortir formatées en texte comme on veut, et le langage n’est pas du awk mais du js à la place.

    • [^] # Re: Dans le même registre jsawk

      Posté par . Évalué à 3.

      Dans le principe, je préfère ça à jq, car cela réutilise une syntaxe déjà familière, qui plus est éprouvée, plutôt que réinventer une nouvelle fois la roue. Mais question de goût personnel, je préfère le Python au javascript.

  • # json_pp

    Posté par (page perso) . Évalué à 2.

    Sans rien installer, json_pp est en général déjà disponible : il vient avec perl 5. Entre autres choses, il permet de « jolifier » un flux JSON.

    • [^] # Re: json_pp

      Posté par . Évalué à 2.

      S'il ne s'agit que d'indenter, json_pp fait l'affaire. Je parlais d'exécuter des expressions sur le JSON, par exemple si le JSON consiste en une liste de dictionnaire, dont seulement un champ nous intéresse, il faut faire pjy '[sub.mon_champ for sub in data]' fichier.json.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.