Testeur JSONPath
Écrivez une expression JSONPath, voyez les correspondances en direct. Syntaxe RFC 9535.
Rien de ce que vous collez ne quitte votre navigateur. La liste d’autorisation connect-src en fait une garantie du navigateur plutôt qu’une promesse. Vérifiez-le vous-même
Écrivez une expression JSONPath, voyez chaque correspondance dans votre propre document, avec le chemin normalisé de chacune. Modifiez et relancez jusqu’à ce qu’elle sélectionne ce que vous vouliez.
La syntaxe utilisée ici suit la RFC 9535, la norme proposée de l’IETF publiée en février 2024. Cela compte plus qu’il n’y paraît, car pendant les dix-sept années précédentes il n’existait aucune spécification.
Pourquoi le dialecte doit être annoncé
JSONPath est né d’un billet de blog de Stefan Goessner en 2007. Il a été largement implémenté et jamais spécifié, et les implémentations ont divergé sur à peu près tout ce qui est intéressant : $.. inclut-il la racine, que signifie un index négatif, $[0,1] fait-il une union, comment se comporte un filtre quand la clé est absente, et que se passe-t-il avec un pas de tranche égal à zéro. Un projet de comparaison a recensé des centaines de ces désaccords.
La RFC 9535 a tranché. Un testeur qui ne dit pas quel dialecte il implémente vous donne la réponse sans vous donner la question, alors celui-ci le dit : RFC 9535, avec les exclusions listées ci-dessous.
Syntaxe prise en charge ici
- $
- La racine du document. Toute expression commence là.
- .name et ['name']
- Un membre nommé. Utilisez la forme à crochets pour les noms comportant des espaces ou de la ponctuation.
- .* et [*]
- Tous les membres d’un objet, ou tous les éléments d’un tableau.
- ..
- Un segment descendant : cherche à ce niveau et à tous les niveaux en dessous.
- [0] et [-1]
- Un index de tableau. Le négatif compte depuis la fin.
- [1:5], [::2], [::-1]
- Une tranche, avec la sémantique de la RFC 9535. Un pas négatif parcourt à rebours.
- [0, 2, 'name']
- Plusieurs sélecteurs dans un même segment, produisant l’union de leurs résultats.
- [?<expression>]
- Un filtre. À l’intérieur, @ est l’élément courant et $ la racine du document. Comparaison avec == != < <= > >=, combinable avec && || et !.
- length() count() match() search() value()
- Les extensions de fonction définies par la RFC 9535. match() ancre toute la chaîne ; search() non.
Délibérément non pris en charge
Les expressions de script de la forme [(...)] n’ont jamais été spécifiées et la RFC 9535 les a supprimées. L’opérateur parent ^ est une extension ajoutée par certaines implémentations et absente de la RFC. Et @.length comme pseudo-propriété est l’orthographe pré-RFC de ce qui est aujourd’hui length(@) ; si vous en collez une, le testeur le dit plutôt que de ne rien renvoyer en silence.
JSONPath, JMESPath, jq et JSON Pointer
Quatre façons d’adresser des parties d’un document JSON, pour quatre besoins différents.
- JSONPath
- Sélectionne un ensemble de nœuds. Le meilleur choix quand vous voulez tout ce qui correspond à un motif, à n’importe quelle profondeur. Désormais normalisé par la RFC 9535.
- JMESPath
- Transforme autant qu’il sélectionne : projections, multiselect hashes et expressions en pipeline permettent de remodeler la sortie. Utilisé par la CLI AWS. Doté d’une vraie spécification dès le départ.
- jq
- Un langage complet auquel on a greffé une syntaxe de requête. À sortir quand l’opération relève plus de la programmation que de la sélection.
- JSON Pointer, RFC 6901
- Adresse exactement un emplacement, sans joker ni filtre. Délibérément trivial, ce qui explique que JSON Patch et JSON Schema l’utilisent tous deux. Deux échappements : ~0 pour un tilde et ~1 pour une barre oblique.
How to do this in code
Lancer la même requête en code.
py Python
# jsonpath-ng is the most complete Python implementation
from jsonpath_ng.ext import parse
expr = parse('$.store.book[?(@.price < 10)].title')
titles = [m.value for m in expr.find(data)]
# JMESPath, if you prefer a specified language with projections
import jmespath
titles = jmespath.search('store.book[?price < `10`].title', data) js JavaScript
import { JSONPath } from 'jsonpath-plus';
const titles = JSONPath({
path: '$.store.book[?(@.price < 10)].title',
json: data,
});
// Get the normalised paths rather than the values
const paths = JSONPath({ path: '$..author', json: data, resultType: 'path' }); sh jq
jq n’a pas d’opérateur descendant-avec-filtre, les deux moitiés s’écrivent donc séparément.
# The jq equivalent of a filtered descendant search
jq '.store.book[] | select(.price < 10) | .title' data.json
# Every value at any depth under a key
jq '.. | .author? // empty' data.json java Java
Jayway JsonPath est antérieur à la RFC 9535 et s’en écarte par endroits, notamment sur les filtres portant sur des clés absentes.
import com.jayway.jsonpath.JsonPath;
List<String> titles = JsonPath.read(json, "$.store.book[?(@.price < 10)].title"); Questions fréquentes
- Pourquoi mon expression ne renvoie-t-elle rien ?
- En général l’une de trois choses : un nom qui doit passer par des crochets avec guillemets parce qu’il contient une espace ou un tiret, un filtre comparant à une chaîne non citée (écrivez @.type == 'book', pas @.type == book), ou un chemin qui suppose un tableau là où le document a un objet. Le testeur signale une erreur d’analyse avec la position quand l’expression elle-même est malformée, et un résultat vide seulement quand l’expression est valide mais ne correspond à rien.
- Qu’est-ce qu’un chemin normalisé ?
- La RFC 9535 définit une écriture canonique pour l’emplacement d’une correspondance : noms entre crochets et guillemets et index numériques, comme dans $['store']['book'][0]['title']. Chaque correspondance en affiche une, ce qui rend les résultats comparables d’une implémentation à l’autre.
- $..* est-il la même chose que $.. ?
- Non, et c’est l’une des divergences que la RFC a tranchées. $..* sélectionne tous les nœuds descendants en excluant la racine ; un $.. seul n’est même pas une expression complète.