Aujourd’hui, JSON est l’un des formats les plus utilisés pour l’échange de données entre les ap­pli­ca­tions, notamment sur Internet. JSONPath est un langage d’ex­pres­sion per­met­tant la lecture de données spé­ci­fiques à partir d’objets JSON. Découvrez avec nous l’im­plé­men­ta­tion JSONPath dé­ve­lop­pée par Python et ses prin­ci­pales ap­pli­ca­tions, pré­sen­tées à l’aide d’exemples simples.

JSONPath dans Python : de quoi s’agit-il ?

JSON est un format de fichiers com­pa­tible avec de nombreux systèmes, qui simplifie et optimise l’échange de données struc­tu­rées entre les ap­pli­ca­tions. Les fichiers JSON sont composés de paires clé-valeur (Key-Value Pairs). Les valeurs peuvent cor­res­pondre à dif­fé­rents types de données, qu’il s’agisse de valeurs pri­mi­tives comme d’objets. Ces objets peuvent aussi renfermer leurs propres paires clé-valeur. Le format JSON étant pris en charge par l’immense majorité des systèmes modernes, il peut être utilisé pour l’échange de données entre des ap­pli­ca­tions de n’importe quel type, que ce soit sur or­di­na­teur (lo­ca­le­ment) ou sur Internet.

Néanmoins, toutes ces ap­pli­ca­tions n’ont pas toujours besoin de l’ensemble des données contenues dans un fichier JSON. Dans ce genre de si­tua­tions, la solution JSONPath peut donc vous être utile. JSONPath est un langage d’ex­pres­sion per­met­tant la lecture ciblée d’in­for­ma­tions spé­ci­fiques à partir d’objets JSON. Dans la plupart des langages de pro­gram­ma­tion, il convient d’importer JSONPath depuis une bi­blio­thèque externe. Comme de telles bi­blio­thèques doivent être im­plé­men­tées sé­pa­ré­ment pour chaque langage, ces bi­blio­thèques ou im­plé­men­ta­tions peuvent être lé­gè­re­ment dif­fé­rentes les unes des autres.

Le module jsonpath-ng dans Python

jsonpath-ng cor­res­pond cer­tai­ne­ment à l’im­plé­men­ta­tion la plus courante de JSONPath dans Python. Python a également développé d’autres im­plé­men­ta­tions JSONPath, notamment jsonpath et jsonpath-rw. Celles-ci restant toutefois moins po­pu­laires et complètes, cet article se concentre donc ex­clu­si­ve­ment sur jsonpath-ng.

Ins­tal­la­tion

Il est très facile d’installer jsonpath-ng en passant par votre shell. Pour ce faire, il vous suffit de ren­seig­ner la commande pip install jsonpath-ng.

Note

L’ins­tal­la­tion s’effectue depuis le ges­tion­naire de paquets pip, que Python utilise par défaut. Si ce ges­tion­naire de paquets n’est pas installé sur votre système, alors vous devez commencer par là. Pour de plus amples in­for­ma­tions à ce sujet, vous pouvez consulter le site Web de pip.

Syntaxe

JSONPath vous permet d’exécuter des requêtes complexes sur des objets JSON. Le module renferme donc plusieurs méthodes, opé­ra­teurs et ex­pres­sions atomiques qui peuvent vous aider à sé­lec­tion­ner et in­ter­ro­ger des données spé­ci­fiques. Les deux méthodes prin­ci­pales utilisées par JSONPath sont parse() et find(). Avec parse(), vous pouvez définir des requêtes pour les ré­fé­ren­cer et les répéter autant que né­ces­saire par la suite. Avec find(), vous pouvez exécuter lesdites requêtes sur des données JSON, de manière à en extraire des valeurs concrètes. Pour mieux com­prendre leur fonc­tion­ne­ment, étudiez l’exemple ci-dessous :

import json
import jsonpath_ng as jp
raw_data = '''
{
    "nom": "John",
    "âge": 30,
    "domicile": "Marseille"
}
'''
json_object = json.loads(raw_data)
name_query = jp.parse("$.nom")
result = name_query.find(json_object)
print(result[0].value) # Résultat : John
Python

Dans l’exemple ci-dessus, les données JSON sous la forme d’une String ont été con­ver­ties en un objet dic­tion­naire grâce à l’uti­li­sa­tion de json.loads. Ce format est en effet le plus efficace pour un travail avec Python. Au moment de la création de name_query, la requête "$.nom" a été définie (pour renvoyer la valeur « nom »). Par la suite, elle a été appliquée à l’objet JSON avec find(). Pour finir, le résultat de la requête a été en­re­gis­tré dans la variable result, avant d’être lu grâce à result[0].value.

Note

Comme le montre l’exemple ci-dessus, il convient également d’intégrer le module Python json de manière à ce que Python puisse lire les données JSON cor­res­pon­dantes à partir d’une String ou d’un fichier JSON. Ces Strings et ces fichiers peuvent être convertis par la suite à l’aide de loads() ou load(), dans un format pouvant être lu par Python.

En plus de renvoyer la valeur demandée, la méthode « find » apporte également d’autres in­for­ma­tions con­tex­tuelles, par exemple le chemin d’accès per­met­tant de trouver la valeur re­cher­chée. Ces in­for­ma­tions sont renvoyées sous la forme d’une liste, la valeur re­cher­chée cor­res­pon­dant à l’index 0. Vous pouvez donc utiliser result[0].value si vous souhaitez afficher la valeur re­cher­chée.

Dans l’exemple ci-dessus, c’est le symbole du dollar (« $ ») qui a été utilisé afin de définir la requête. Il s’agit donc d’une ex­pres­sion atomique qui permet de faire référence à l’objet racine JSON. L’ensemble des opé­ra­teurs et des ex­pres­sions atomiques est ré­per­to­rié dans le tableau ci-dessous.

Ex­pres­sion/opérateur Sig­ni­fi­ca­tion Exemple Ex­pli­ca­tion
$ Objet racine $.marc.âge Permet d’accéder à la valeur de la clé « âge » de l’objet « marc ».
. Champ d’un objet $.marc Permet d’accéder à « marc », « marc » cor­res­pon­dant à un champ de l’objet racine.
.. Recherche récursive d’un champ (les champs des sous-objets font aussi l’objet d’un examen) $.people..âge Permet de renvoyer toutes les oc­cur­rences du champ « âge » dans « people » et ses sous-objets.
[x] Élément dans un tableau $.people[5] Permet d’accéder au sixième élément (cor­res­pon­dant à l’index 5) du tableau « people ».
\* Caractère générique pour un nombre (gé­né­ra­le­ment utilisé en lien avec des boucles for) $.people[\*] Permet d’accéder à un champ dans « people ». En cas de com­bi­nai­son avec une boucle for, chaque champ serait renvoyé dans l’ordre.

Outre les ex­pres­sions et les opé­ra­teurs, certains filtres vous per­met­tent également de spécifier encore votre recherche. Dans l’im­plé­men­ta­tion JSONPath dé­ve­lop­pée par Python, ces filtres peuvent être liés à des opé­ra­teurs Python. Tous les symboles pouvant être associés à ces filtres sont présentés, à l’aide d’exemples, dans le tableau suivant.

Symbole Sig­ni­fi­ca­tion Exemple Ex­pli­ca­tion
.[?(filter)] Syntaxe générale pour les filtres (les pa­ren­thèses ne sont pas obli­ga­toires) $.people[?(@.name == "Anne")] Permet d’accéder aux personnes ayant pour prénom « Anne ».
@ Objet soumis à examen (souvent utilisé dans le contexte d’une boucle for) $.people[?(@.âge < 50)] Permet d’accéder aux champs de « people » dont la valeur présente dans « âge » est in­fé­rieure à 50.
<, >, <=, >=, == et != Opé­ra­teurs de com­pa­rai­son (per­met­tant de filtrer certains résultats de recherche) $.people[@.âge < 50 & @.âge > 20] Permet d’accéder aux personnes dont l’âge est compris entre 20 et 50 ans.
& ET logique $.people[?(@.domicile == ’Paris’ & @.âge > 40)] Permet d’accéder aux personnes dont l’âge est supérieur à 40 ans ET qui résident à Paris.
Note

Vous souhaitez utiliser des filtres ? Optez pour le module jsonpath_ng.ext, puis faites-y référence au moment de l’appel de parse().

Exemple d’ap­pli­ca­tion pour JSONPath dans Python

import json
import jsonpath_ng as jp
# JSON-Daten als String
import json
import jsonpath_ng as jp
# JSON-Daten als String
data = """
{
    "villes": [
        {
            "nom": "Paris",
            "région": "Île-de-France",
            "habitants": 2145000,
            "estunecapitale": true,
            "15ième arrondissement": {
                "habitants": 230000    
            }
        },
        {
            "nom": "Marseille",
            "région": "Provence-Alpes-Côte d'Azur",
            "habitants": 873000,
            "estunecapitale": false
        },
        {
            "nom": "Lyon",
            "région": "Rhône-Alpes",
            "habitants": 537000,
            "estunecapitale": false
        },
        {
            "nom": "Lille",
            "région": "Hauts-de-France",
            "habitants": 236000
        }
    ]
}
"""
# Convertir des données de String en object dictionnaire
json_data = json.loads(data)
# Demande : noms de toutes les villes
query1 = jp.parse("villes[*].nom")
for match in query1.find(json_data):
    print(match.value)     # Résultat : Paris, Marseille, Lyon, Lille
# importer jsonpath_ng.ext pour appliquer les filtres
import jsonpath_ng.ext as jpx
# Demande : noms de toutes les villes de moins de 1 million d'habitants
query2 = jpx.parse("$.villes [?@.habitants < 1000000].nom")
for match in query2.find(json_data):
    print(match.value)     # Résultat : Marseille, Lyon, Lille
# Tous les champs avec "habitants"
query3 = jp.parse("$.villes..habitants")
match = query3.find(json_data)
for i in match:
    print(i.value)     # Résultat : 2145000, 230000, 873000, 537000, 236000
# Les noms de toutes les villes qui ne s'appellent pas "Marseille"
query4 = jpx.parse('$.villes[?(@.nom != "Marseille")].nom')
for match in query4.find(json_data):
    print(match.value)     # Résultat : Paris, Lyon, Lille
Python

Dans cet exemple, les données JSON prennent la forme d’une String, avant d’être con­ver­ties en un objet dic­tion­naire grâce à loads(). L’objet racine contient uni­que­ment un tableau, ren­fer­mant lui-même quatre villes. Pour chaque ville, il existe quatre champs cor­res­pon­dant aux données suivantes :

  • le nom de la ville ;
  • la région dans laquelle elle se trouve ;
  • son nombre d’habitants ;
  • une in­di­ca­tion précisant s’il s’agit ou non de la capitale de la France.

La ville de Paris est dotée d’un champ sup­plé­men­taire du nom de « 15ième ar­ron­dis­se­ment », auquel un nombre d’habitants est également associé.

Une fois les données con­ver­ties dans un format approprié, quatre dif­fé­rentes sont exécutées. Leurs modes de fonc­tion­ne­ment et résultats sont détaillés di­rec­te­ment dans l’exemple à l’aide de com­men­taires. Comme vous l’avez peut-être remarqué, la troisième requête renvoie cinq valeurs dif­fé­rentes ; en effet, l’opérateur .. recherche les champs cor­res­pon­dants de façon récursive. Tous les objets sont ainsi examinés, y compris l’ensemble des « enfants » desdits objets. Ainsi, le nombre d’habitants dans l’ar­ron­dis­se­ment de Pankow apparaît à côté du nombre d’habitants dans les autres villes.

Conseil

Cette solution alliant JSON à Python constitue un outil po­ly­va­lent dans le domaine de la pro­gram­ma­tion Internet. Vous souhaitez publier ra­pi­de­ment, fa­ci­le­ment et di­rec­te­ment une ap­pli­ca­tion Web en passant par Git ? La méthode Deploy Now proposée par IONOS est parfaite pour vous.

Aller au menu principal