Skip to content
Retour au blog
Tutoriels

Syntaxe JSONPath : interroger et filtrer du JSON (exemples)

Maîtrisez la syntaxe JSONPath : racine, descente récursive, jokers, tranches et filtres, avec exemples. Testez chaque requête dans votre navigateur.

11 min de lecture

Syntaxe JSONPath : interroger et filtrer du JSON (exemples)

JSONPath est un langage de requête pour JSON, exactement comme XPath en est un pour XML. Vous écrivez une expression de chemin et l’évaluateur renvoie chaque valeur qui correspond. Pour récupérer tous les noms d’auteurs d’un document de librairie, il suffit d’écrire $.store.book[*].author, et vous obtenez la liste des auteurs, sans aucun code de parcours.

Ce guide passe en revue chaque sélecteur de la syntaxe JSONPath avec des exemples prêts à copier que vous pouvez exécuter au fil de la lecture. Une précision d’emblée : il existe deux dialectes. Le dialecte Goessner de 2007 est le classique de fait, et RFC 9535 est la norme formelle de l’IETF publiée en février 2024. Ils s’accordent sur les chemins courants et divergent dans les cas limites ; ce guide signale les différences au fur et à mesure. Vous pouvez essayer chacune des expressions ci-dessous dans le JSONPath Tester et basculer entre les deux moteurs pour les comparer.

Voici la fiche-mémo des sélecteurs pour démarrer. Le reste de l’article développe chaque ligne avec un exemple concret appliqué à un seul document JSON partagé.

SélecteurSignificationExemple
$Racine du document$
@Élément courant (dans les filtres)[?(@.price < 10)]
.name / ['name']Membre enfant$.store.book
..Descente récursive$..author
*Tous les éléments / membres$.store.book[*]
[0]Index de tableau$.store.book[0]
[start:end:step]Tranche de tableau (semi-ouverte)$.store.book[0:2]
[a,b]Union de noms / d’index$.store.book[0,2]
[?()]Expression de filtre$.store.book[?(@.price < 10)]
length() count() match() search() value()Fonctions RFC 9535 (uniquement en filtre)[?length(@.title) > 15]

Qu’est-ce que JSONPath ?

JSONPath est un langage de requête déclaratif qui sélectionne des nœuds dans un document JSON. Au lieu d’écrire une boucle qui parcourt objets et tableaux, vous décrivez l’emplacement voulu par un chemin, et l’évaluateur renvoie les valeurs correspondantes. Le modèle mental est le même que celui qu’XPath offre pour XML : un chemin fait de sélecteurs qui traversent la structure pas à pas.

On le retrouve partout où les développeurs touchent à du JSON. Vous l’utilisez pour extraire un champ d’une réponse d’API, pour faire une assertion sur une valeur dans un test d’intégration, pour adresser des champs dans des configs de pipeline pour Kubernetes, AWS Step Functions et Azure Logic Apps, et pour extraire des données de JSON volumineux ou irréguliers sans écrire vous-même la logique de parcours.

Un mot d’histoire, car il explique la scission entre dialectes. Stefan Goessner a proposé JSONPath en 2007. Il s’est répandu vite et est devenu une norme de fait, mais il n’a jamais été formellement spécifié, si bien que les implémentations ont divergé sur les détails. L’IETF a comblé ce vide en février 2024 avec RFC 9535, la première spécification formelle de JSONPath. Les deux dialectes sont vivants aujourd’hui, et c’est pourquoi une même expression peut se comporter différemment selon la bibliothèque qui l’exécute.

Avant de commencer à interroger, il est utile de lire la structure. Mettez en forme une entrée brouillonne avec le JSON Formatter pour rendre l’imbrication visible.

Le document d’exemple

Tous les exemples ci-dessous s’appliquent au JSON classique de librairie de Goessner. Collez-le une fois et réutilisez-le :

{
  "store": {
    "book": [
      { "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
      { "title": "Sword of Honour", "author": "Evelyn Waugh", "price": 12.99 },
      { "title": "Moby Dick", "author": "Herman Melville", "price": 8.99 },
      { "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

Quatre livres avec un titre, un auteur et un prix, plus une bicyclette. Gardez ceci en tête : les prix sont 8.95, 12.99, 8.99 et 22.99, et c’est ce qui détermine les résultats des filtres plus loin.

Racine, enfant et descente récursive ($ . ..)

Chaque expression commence à la racine, notée $. De là, vous descendez dans les enfants avec un point ou avec la notation à crochets ; les deux sont équivalentes :

$.store.book          → the book array
$['store']['book']    → identical result

La notation à crochets est ce qu’il vous faut lorsqu’une clé contient des espaces, des points ou d’autres caractères spéciaux : $['first name'] fonctionne là où $.first name échouerait.

L’opérateur .. est la descente récursive. Il cherche à chaque niveau du document, pas seulement parmi les enfants directs, et collecte tout ce qui correspond au sélecteur suivant, à n’importe quelle profondeur :

$..author
→ ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"]

Quand utiliser .. plutôt qu’un chemin complet

La descente récursive est pratique mais grossière. $..price correspond à chaque prix où qu’il se trouve dans l’arbre, y compris store.bicycle.price, que vous ne vouliez peut-être pas. Quand vous connaissez la forme, explicitez le chemin pour que la requête reste précise :

$..price                  → [8.95, 12.99, 8.99, 22.99, 19.95]  (includes the bicycle)
$.store.book[*].price     → [8.95, 12.99, 8.99, 22.99]         (only books)

Réservez .. aux structures réellement irrégulières ou inconnues. Le compromis est commodité contre contrôle : plus vous en savez sur vos données, plus vous devriez préférer un chemin explicite.

Jokers, index et tranches de tableau (* [0] [start:end:step])

Le joker * sélectionne tous les éléments d’un tableau ou tous les membres d’un objet :

$.store.book[*].title
→ ["Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings"]

Les index de tableau commencent à zéro, et les index négatifs comptent depuis la fin :

$.store.book[0].title     → ["Sayings of the Century"]
$.store.book[-1].title    → ["The Lord of the Rings"]

Les tranches utilisent la même convention semi-ouverte [start:end:step] que Python et JavaScript : start est inclus, end est exclu.

$.store.book[0:2].title   → ["Sayings of the Century", "Sword of Honour"]

Cela renvoie deux livres, pas trois : l’index 0 et l’index 1, en s’arrêtant avant l’index 2. La borne de fin exclusive est à elle seule le bug JSONPath le plus courant, alors autant le graver dans la mémoire :

[0:2]   → first TWO elements (indices 0, 1)   ← correct
[0:3]   → first THREE elements (indices 0, 1, 2)

Omettez une borne pour aller jusqu’au bout, et ajoutez un pas pour prendre un élément sur N :

$.store.book[2:].title    → ["Moby Dick", "The Lord of the Rings"]
$.store.book[:3].title    → first three titles
$.store.book[::2].title   → ["Sayings of the Century", "Moby Dick"]  (every other)

Expressions de filtre [?()] — la partie puissante

C’est avec les filtres que JSONPath prend toute sa valeur. Un filtre [?()] ne conserve que les éléments pour lesquels un prédicat est vrai, et à l’intérieur du filtre, @ désigne l’élément courant testé.

Pour sélectionner les livres à moins de 10 :

$.store.book[?(@.price < 10)].title
→ ["Sayings of the Century", "Moby Dick"]

Face aux prix de la librairie (8.95, 12.99, 8.99, 22.99), deux livres remplissent la condition. Voici comment construire un prédicat de filtre étape par étape :

  1. Comparez à un littéral. Utilisez ==, !=, <, <=, >, >=, par exemple @.price > 10.
  2. Faites correspondre une chaîne. Les littéraux de chaîne prennent des guillemets simples : @.author == 'Nigel Rees'.
  3. Testez l’existence. Une référence de membre seule sélectionne les éléments qui la possèdent : [?(@.isbn)] ne conserve que les livres ayant un isbn.
  4. Combinez les conditions. Reliez les prédicats avec && et || : [?(@.price < 10 && @.author == 'Herman Melville')].

L’erreur de filtre la plus fréquente porte sur la portée de @. À l’intérieur du prédicat, l’élément courant est @, pas $. Écrire $.price renvoie vers la racine du document, pas vers le livre testé :

$.store.book[?($.price < 10)]   → wrong scope, matches nothing useful
$.store.book[?(@.price < 10)]   → correct: each book's own price

RFC 9535 contre Classic dans les filtres

Les deux dialectes se séparent sur les espaces et les guillemets. Classic est permissif : [?(@.price<10)] sans espaces se parse sans problème. RFC 9535 suit sa grammaire à la lettre et se montre plus strict sur l’écriture du filtre. Si un filtre qui fonctionnait ailleurs échoue, vérifiez l’espacement et le moteur. Gardez vos filtres propres (opérateurs entourés d’espaces, chaînes entre guillemets simples) et ils s’évalueront de la même façon quelle que soit la bibliothèque qui finit par les exécuter.

Sélecteurs d’union — sélectionner plusieurs clés à la fois ([a,b])

Un sélecteur d’union liste plusieurs noms ou index dans un même crochet et les rassemble tous :

$.store.book[0]['title','author']
→ ["Sayings of the Century", "Nigel Rees"]

Les unions fonctionnent aussi avec des index, et vous pouvez les mélanger à d’autres sélecteurs pour une projection figée :

$.store.book[0,2].title          → ["Sayings of the Century", "Moby Dick"]
$.store.book[*]['title','price']  → title and price of every book

Les unions sont l’outil adapté quand vous voulez quelques champs précis plutôt qu’un objet entier ou un balayage par joker.

Fonctions RFC 9535 : length, count, match, search, value

RFC 9535 définit cinq extensions de fonctions standard. La règle qui piège presque tout le monde, et que les guides concurrents énoncent constamment de travers, est celle-ci :

Ces fonctions ne sont appelables qu’à l’intérieur d’un filtre [?...], jamais comme segment de chemin autonome.

Écrire $.store.book.length() n’est pas du RFC 9535 valide, et la grammaire standard le rejette. Cette forme d’appel en segment est une extension de jsonpath-plus, pas une partie de la spécification. Pour filtrer par longueur, vous appelez la fonction à l’intérieur du prédicat :

$.store.book[?length(@.title) > 15]
→ [
    { "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
    { "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
  ]

Les deux titres sélectionnés font plus de 15 caractères ; « Moby Dick » (9) et « Sword of Honour » (15, pas plus de 15) sont exclus.

Voici ce que fait chaque fonction à l’intérieur d’un filtre :

  • length() — longueur d’une chaîne, d’un tableau ou d’un objet : [?length(@.title) > 15]
  • count() — nombre de nœuds dans une liste de nœuds : [?(count(@.authors) > 1)]
  • match() — test regex sur la chaîne entière (motif I-Regexp) : [?match(@.author, 'J.*')]
  • search() — test regex sur une sous-chaîne : [?search(@.title, 'the')]
  • value() — convertit une liste à un seul nœud en sa valeur pour la comparaison

Les cinq relèvent d’une fonctionnalité RFC 9535. Le dialecte Classic (Goessner) ne les implémente pas ; donc si une expression à base de fonction échoue, vérifiez que vous l’appelez bien dans un filtre et que votre moteur est réglé sur RFC 9535.

RFC 9535 contre Classic Goessner — pourquoi la même expression diffère

Quand une expression JSONPath renvoie des résultats différents dans deux outils, le dialecte en est généralement la cause. Voici comment les deux se comparent :

AspectClassic Goessner (2007)RFC 9535 (2024)
NormalisationDe fait, jamais formaliséePremière spécification formelle de l’IETF
Espaces/guillemets dans les filtresPermissif ([?(@.price<10)] accepté)Strict, suit la grammaire à la lettre
Comparaison de membre absentDépend de l’implémentationBien définie, ne lève pas d’erreur
Fonctions standardHors du dialectelength count match search value
Chemins normalisésPas de forme canoniqueCanonique, notation à crochets et guillemets simples
Ordre des unionsVarie selon la bibliothèqueSpécifié

Conseil pratique : si votre système en aval annonce une conformité RFC 9535, écrivez et validez contre le moteur standard. Si vous maintenez une expression copiée de jsonpath.com, jsonpath-plus ou d’un service basé sur Jayway, utilisez Classic pour que les résultats se reproduisent. Le JSONPath Tester fait tourner les deux moteurs derrière un seul interrupteur, ce qui vous permet de coller une expression une fois et de voir comment chaque dialecte la traite côte à côte. Cette comparaison à double moteur est le moyen le plus rapide de diagnostiquer une divergence.

JSONPath, XPath ou jq — lequel choisir

On confond souvent ces trois-là, voici donc la version courte :

  • JSONPath est une requête de chemin déclarative pour JSON. Il est idéal embarqué dans des configs et des assertions de test, là où vous voulez nommer un emplacement sans écrire de code.
  • XPath en est l’équivalent dans le monde XML. JSONPath lui a emprunté une partie de sa notation (*, .., []), ce qui justifie l’analogie, mais les langages ne sont pas interchangeables et leurs jeux de fonctions diffèrent.
  • jq est un processeur JSON en ligne de commande. Il va bien au-delà de la sélection de chemin (transformation, agrégation, remodelage) et vit dans votre pipeline shell.

Le choix est en général net. Pour une assertion embarquée ou un champ de config de pipeline, tournez-vous vers JSONPath. Pour la transformation et le traitement de données pilotés par le shell, tournez-vous vers jq ; la fiche-mémo jq couvre ce flux de travail en détail. Et lorsque la question est de savoir si une charge utile est conforme à une forme attendue, plutôt que de savoir où se trouve un champ, validez-la avec le JSON Schema Validator et son guide de validation complet.

7 erreurs JSONPath courantes

  1. Oublier la racine $. store.book est rejeté par la plupart des moteurs ; chaque expression commence par $.
  2. Décalage d’un cran sur la tranche. [0:2] correspond à deux éléments, pas à trois ; la borne de fin est exclusive.
  3. Mauvais dialecte. Exécuter une expression Classic sous RFC 9535 (ou l’inverse) peut provoquer une erreur de parse ou faire correspondre des nœuds différents. Réglez le moteur en conséquence.
  4. Fonction en segment autonome. $.store.book.length() est invalide en RFC 9535 ; appelez length() à l’intérieur d’un filtre.
  5. Oublier @ dans un filtre. [?($.price < 10)] pointe vers la racine ; utilisez [?(@.price < 10)].
  6. Mauvais usage des guillemets dans les crochets. $[store] est une erreur ; mettez la clé entre guillemets : $['store'].
  7. Croire que .. s’arrête au premier niveau. La descente récursive correspond à chaque profondeur, pas seulement aux enfants directs.

Tester JSONPath en ligne, en toute confidentialité

Le moyen le plus rapide d’apprendre la syntaxe JSONPath est de l’exécuter. Le JSONPath Tester évalue en direct chaque expression de ce guide : deux moteurs RFC 9535 et Classic, les vues de résultat Values / Paths / Both, des chemins normalisés pour le débogage, et une exécution 100 % dans le navigateur, sans téléversement, sans inscription et sans eval, donc sûre pour des charges utiles propriétaires. Construisez un chemin ici, confirmez qu’il sélectionne exactement les nœuds voulus, puis collez l’expression validée directement dans votre code, vos tests ou votre pipeline.

Pour le reste du flux de travail JSON, transformez une réponse d’exemple en interfaces typées avec JSON to TypeScript, ou comparez deux documents champ par champ avec JSON Diff.

Foire aux questions

À quoi sert JSONPath ?

JSONPath interroge du JSON sans code impératif. Les développeurs s’en servent pour extraire des champs de réponses d’API, faire des assertions sur des valeurs dans des tests d’intégration et adresser des champs dans des configs pour Kubernetes, AWS Step Functions et Azure Logic Apps. Il excelle à extraire des données de structures volumineuses ou irrégulières où écrire un parcours à la main serait fastidieux.

Quelle est la différence entre RFC 9535 et le JSONPath classique ?

Classic est le dialecte de fait de Stefan Goessner de 2007, largement implémenté mais jamais formellement spécifié, d’où la divergence des bibliothèques. RFC 9535 est la spécification formelle de l’IETF de février 2024 : elle définit une grammaire précise, des chemins normalisés pour les résultats et cinq fonctions standard. Les deux diffèrent aux marges dans les filtres, les unions et la comparaison de membre absent.

Comment fonctionnent les expressions de filtre JSONPath ?

Un filtre [?()] ne conserve que les éléments dont le prédicat est vrai, et @ désigne l’élément courant. Par exemple, $.store.book[?(@.price < 10)] sélectionne les livres à moins de 10. Vous pouvez combiner des conditions avec && et ||, tester si un membre existe et comparer à des littéraux de chaîne ou de nombre.

Puis-je utiliser length() comme $.store.book.length() ?

Non. En RFC 9535, length() et les quatre autres fonctions ne sont appelables qu’à l’intérieur d’un filtre, comme $.store.book[?length(@.title) > 15]. La forme en segment autonome $.store.book.length() est une extension de jsonpath-plus, pas du JSONPath standard, et la grammaire RFC 9535 la rejette.

JSONPath est-il identique à XPath ?

Non, mais l’idée est proche. XPath interroge du XML ; JSONPath interroge du JSON ; tous deux localisent des nœuds avec des sélecteurs de chemin. JSONPath a délibérément emprunté une partie de la notation d’XPath (*, .. et []), ce qui rend l’analogie utile, mais la syntaxe, la sémantique et les jeux de fonctions sont différents et non interchangeables.

Que fait la descente récursive (..) en JSONPath ?

L’opérateur .. cherche à chaque niveau du document, pas seulement parmi les enfants directs. $..author collecte chaque membre author où qu’il apparaisse, à n’importe quelle profondeur d’imbrication. C’est le moyen le plus rapide d’extraire un champ d’une structure profondément imbriquée ou irrégulière, mais il peut faire correspondre bien plus de nœuds que prévu, alors restreignez-le quand vous le pouvez.

Tags: jsonpath json query-language rfc-9535 filter-expression developer-tools

Articles connexes

Voir tous les articles