Travailler avec les données JSON en Python

Pour suivre nos dernières actualités n'oubliez pas de vous abonner :
Publicité :

Depuis sa création, JSON est rapidement devenu la norme de facto pour l'échange d'informations.

 Il y a de fortes chances que vous soyez ici parce que vous avez besoin de transporter des données d'un endroit à l’autre. 

Vous rassemblez peut-être des informations par le biais d'une API ou stockez vos données dans une base de données documentaire.

D'une façon ou d'une autre, vous êtes jusqu'au cou en JSON. Et votre objectif c’est de vous en sortir au travers de Python.

Heureusement, il s'agit d'une tâche assez fréquente et, comme la plupart des tâches courantes, c'est incroyablement facile en Python. 

Ne craignez rien, compagnons Pythoniens et Pythonistes. ça va être un jeu d'enfant !


Donc, nous utilisons JSON pour stocker et échanger des données ?

Oui, tu as compris ! Ce n'est rien de plus qu'un format standardisé que la communauté utilise pour faire circuler les données.

Gardez à l'esprit que JSON n'est pas le seul format disponible pour ce genre de travail, mais XML et YAML sont probablement les seuls autres qui méritent de se voir mentionner


Une (très) brève histoire de JSON

Comme on pouvait s'y attendre, JavaScript Object Notation a été inspiré par un subset du langage de programmation JavaScript traitant de la syntaxe littérale d'objet.

Ils ont un site web génial qui explique tout. Mais ne t'inquiète pas : JSON est depuis longtemps devenu agnostique un  standard, donc nous pouvons heureusement éviter de passer  par JavaScript pour les besoins de cette discussion.

En fin de compte, la communauté dans son ensemble a adopté la syntaxe JSON parce qu'elle est facile à créer et à comprendre tant pour les humains que pour les machines.

Sa taille (mémoire) est aussi moins importante que pour du xml.

Look, it’s JSON!

Préparez-vous.

Je suis sur le point de vous montrer du véritable JSON, comme vous le constateriez sur le terrain.

JSON est censé être lisible par n'importe qui qui a utilisé un langage de style C, et Python est un langage de style C.


{

    "firstName": "Jane",

    "lastName": "Doe",

    "hobbies": ["running", "sky diving", "singing"],

    "age": 35,

    "children": [

        {

            "firstName": "Alice",

            "age": 6

        },

        {

            "firstName": "Bob",

            "age": 8

        }

    ]

}


Comme vous pouvez le voir, JSON supporte les types primitifs, comme les chaînes et les nombres, ainsi que les listes et objets imbriqués.

Attendez, ça ressemble à un dictionnaire Python !

Oui ! c'est à peu près la notation universelle des objets à ce stade

Python supporte JSON en natif !

Python est livré avec un paquet intégré appelé json pour encoder et décoder les données JSON. Mets ce petit bout de code en haut de ton script python :

-------------------------
import json
-------------------------

Un peu de vocabulaire

Le processus d'encodage de JSON est généralement appelé sérialisation. Ce terme désigne la transformation de données en une série d'octets (donc en série) à stocker ou à transmettre sur un réseau.

Vous entendrez peut-être aussi le terme marshaling, mais il s'agit d'une toute autre discussion.

Naturellement, la désérialisation est le processus réciproque de décodage des données qui ont été stockées ou fournies dans le standard JSON.

Yikes ! Ça a l'air plutôt technique. Définitivement. Mais en réalité, il ne s'agit ici que de lire et d'écrire. Pensez-y comme ceci : l'encodage sert à écrire des données sur le disque, tandis que le décodage sert à lire les données en mémoire.

Sérialisation JSON

Que se passe-t-il après qu'un ordinateur a traité beaucoup d'informations ? Il a besoin de faire un transfert de données.

En conséquence, la bibliothèque json expose la méthode dump() pour écrire des données dans des fichiers. Il y a aussi une méthode dumps() (prononcée comme "dump-s") pour écrire dans une chaîne Python.

Les objets Python simples sont traduits en JSON selon une conversion assez intuitive.

<table style="width: 242.113px;">
<tbody>
<tr>
<td style="width: 137px;">
<p><strong> Python </strong></p>
</td>
<td style="width: 114.113px;">
<p><strong> JSON </strong></p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>dict</p>
</td>
<td style="width: 114.113px;">
<p>object</p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>list, tuple</p>
</td>
<td style="width: 114.113px;">
<p>array</p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>str</p>
</td>
<td style="width: 114.113px;">
<p>string</p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>int, long, float</p>
</td>
<td style="width: 114.113px;">
<p>number</p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>True</p>
</td>
<td style="width: 114.113px;">
<p>true</p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>False</p>
</td>
<td style="width: 114.113px;">
<p>false</p>
</td>
</tr>
<tr>
<td style="width: 137px;">
<p>None</p>
</td>
<td style="width: 114.113px;">
<p>null</p>
</td>
</tr>
</tbody>
</table>

Un exemple simple de sérialisation

Imaginez que vous travaillez avec un objet Python en mémoire qui ressemble un peu à ceci :

----------------------------------------------------

data = {

    "president": {

        "name": "Zaphod Beeblebrox",

        "species": "Betelgeusian"

    }

}

---------------------------------------------------

Il est essentiel que vous sauvegardez ces informations sur disque, votre mission est donc de les écrire dans un fichier.

En utilisant le gestionnaire de contexte de Python, vous pouvez créer un fichier appelé data_file.json et l'ouvrir en mode écriture. (Les fichiers JSON se terminent commodément par une extension.json.)

------------------------------------------------------------------

with open("data_file.json", "w") as write_file:

    json.dump(data, write_file)

------------------------------------------------------------------

Notez que dump() prend deux arguments positionnels : (1) l'objet de données à sérialiser, et (2) l'objet de type fichier dans lequel les octets seront écrits.

Ou, si vous aviez envie de continuer à utiliser ces données JSON sérialisées dans votre programme, vous pourriez l'écrire dans un objet Str Python natif.

------------------------------------------

json_string = json.dumps(data)

------------------------------------------

Notez que l'objet de type fichier est absent puisque vous n'êtes pas en train d'écrire sur le disque. Sinon, dumps() est comme dump().

Des keywords arguments bien utiles

Rappelez-vous, JSON est destiné à être facilement lisible par les humains, mais la syntaxe n'est pas suffisante si tout est enchevêtré dans un ensemble compact 

 De plus, vous avez probablement un style de programmation différent du mien, et il pourrait être plus facile pour vous de lire le code quand il est formaté à votre guise.

NOTE : Les deux méthodes dump() et dumps() utilisent les mêmes keyword arguments

La première option que la plupart des gens veulent changer est l'espace.

Vous pouvez utiliser l'argument indent keyword pour spécifier la taille de l'indentation pour les structures imbriquées.

Vérifiez la différence par vous-même en utilisant les données, que nous avons définies ci-dessus, et en exécutant les commandes suivantes dans une console :

--------------------------------------------------------

>>> json.dumps(data)

>>> json.dumps(data, indent=4)

--------------------------------------------------------

Une autre option de formatage est l'argument du mot-clé séparateurs.

Par défaut, il s'agit d'un séparateur à 2 tuples (", ", ", " : "), mais une alternative courante pour compact JSON  est (",",", " :").

Jetez un coup d'œil à l'échantillon JSON pour voir où ces séparateurs entrent en jeu.

Il y en a d'autres, comme sort_keys, mais je n'ai aucune idée de ce que fait celui-là. Vous pouvez trouver une liste complète dans la documentation si vous êtes curieux.

La désérialisation JSON

Maintenant, il est temps de le mettre en forme.

Dans la bibliothèque json, vous trouverez load() et loads() pour transformer les données codées JSON en objets Python.

Tout comme la sérialisation, il existe une table de conversion simple pour la désérialisation, bien que vous puissiez probablement deviner à quoi elle ressemble déjà.

<p>&nbsp;</p>
<table style="width: 218.123px;">
<tbody>
<tr>
<td style="width: 132px;">
<p><strong>JSON</strong></p>
</td>
<td style="width: 88.1226px;">
<p><strong>Python</strong></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">object</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">dict</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">array</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">list</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">string</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">str</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">number (int)</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">int</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">number (real)</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">float</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">true</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">True</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">false</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">False</span></p>
</td>
</tr>
<tr>
<td style="width: 132px;">
<p><span style="font-weight: 400;">null</span></p>
</td>
<td style="width: 88.1226px;">
<p><span style="font-weight: 400;">None</span></p>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>

Techniquement, cette conversion n'est pas une parfaite inverse de la table de sérialisation.

Cela signifie essentiellement que si vous encodez un objet maintenant et que vous le décodez à nouveau plus tard, vous ne récupérerez peut-être pas exactement le même objet.

J'imagine que c'est un peu comme la téléportation : décomposer mes molécules ici et les remettre ensemble là-bas. Est-ce que je serais toujours la même personne ?

En réalité, c'est probablement plus comme si un ami traduisait quelque chose en japonais et un autre ami le traduisait en anglais.

Quoi qu'il en soit, l'exemple le plus simple serait d'encoder un tuple et de récupérer une liste après décodage, comme ça :

---------------------------------------------------------------------------

>>> blackjack_hand = (8, "Q")

>>> encoded_hand = json.dumps(blackjack_hand)

>>> decoded_hand = json.loads(encoded_hand)

>>> blackjack_hand == decoded_hand

False

>>> type(blackjack_hand)

<class 'tuple'>

>>> type(decoded_hand)

<class 'list'>

>>> blackjack_hand == tuple(decoded_hand)

True

------------------------------------------------------------------------------

Un exemple simple de désérialisation

Cette fois, imaginez que vous avez des données stockées sur disque que vous aimeriez manipuler en mémoire.

Vous utiliserez toujours le gestionnaire de contexte, mais cette fois vous ouvrirez le fichier data_file.json existant en mode lecture.

------------------------------------------------------------------

with open("data_file.json", "r") as read_file:

    data = json.load(read_file)

------------------------------------------------------------------

Les choses sont assez simples ici, mais gardez à l'esprit que le résultat de cette méthode pourrait retourner n'importe lequel des types de données autorisés de la table de conversion.

Ceci n'est important que si vous chargez des données que vous n'avez jamais vues auparavant.

Dans la plupart des cas, l'objet racine sera un dict ou une liste.

Si vous avez extrait des données JSON d'un autre programme ou si vous avez obtenu une chaîne de données formatées JSON en Python, vous pouvez facilement désérialiser cela avec loads(), qui charge naturellement à partir d'une chaîne :

------------------------------------------------------------

json_string = """

{

    "researcher": {

        "name": "Ford Prefect",

        "species": "Betelgeusian",

        "relatives": [

            {

                "name": "Zaphod Beeblebrox",

                "species": "Betelgeusian"

            }

        ]

    }

}

"""

data = json.loads(json_string)

--------------------------------------------------------------

Un exemple quasiment réel

Pour votre exemple d'introduction, vous utiliserez JSONPlaceholder, une excellente source de fausses données JSON à des fins pratiques.

Créez d'abord un fichier script appelé scratch.py, ou ce que vous voulez. Je ne peux pas vraiment t'arrêter.

Vous devrez faire une demande d'API au service JSONPlaceholder, alors utilisez simplement la librairie requests pour faire le gros du travail. Ajoutez ces imports en haut de votre fichier :

---------------------------------------
import json
import requests
---------------------------------------


Maintenant, vous allez travailler avec une liste de TODOs parce que... vous savez, c'est un rite d'aller de l'avant et de faire une requête à l'API JSONPlaceholder pour le terminal /todos.

Si vous n'êtes pas familier avec les requêtes, il existe en fait une méthode json() pratique qui fera tout le travail pour vous, mais vous pouvez vous entraîner à utiliser la bibliothèque json pour désérialiser l'attribut texte de la réponse object.passage ou autre.

Ça devrait ressembler à quelque chose comme ça :

-----------------------------------------------------

response = requests.get("https://jsonplaceholder.typicode.com/todos")

todos = json.loads(response.text)

-----------------------------------------------------

Tu ne crois pas que ça marche ? Très bien, exécute le fichier en mode interactif et teste-le par toi-même. Pendant que tu y es, vérifie le type de todos.

 Si tu te sens aventureux, jette un coup d'œil aux 10 premiers articles de la liste.

-------------------------------------------------------

>>> todos == response.json()

True

>>> type(todos)

<class 'list'>

>>> todos[:10]

...

-------------------------------------------------------------

Je ne peux pas te mentir, mais je suis content que tu sois sceptique.

Qu'est-ce que le mode interactif ? Ah, j'ai cru que tu n'oserais jamais demander ! Tu sais que tu sautes toujours entre ton éditeur et le terminal ? Eh bien, nous, les Pythoniens sournois, utilisons le drapeau interactif -i lorsque nous exécutons le script.

C'est un petit truc génial pour tester le code parce qu'il exécute le script et ouvre ensuite une invite de commande interactive avec accès à toutes les données du script !

Très bien, c'est l'heure de l'action. Tu peux voir la structure des données en visitant le terminal dans un navigateur, mais voici un exemple de TODO :

-----------------------------------------------------

{

    "userId": 1,

    "id": 1,

    "title": "delectus aut autem",

    "completed": false

}

-------------------------------------------------------

Il y a plusieurs utilisateurs, chacun avec un userId unique, et chaque tâche a une propriété booléenne complétée.

Pouvez-vous déterminer quels utilisateurs ont effectué le plus grand nombre de tâches ?


<p><iframe src="https://trinket.io/embed/python3/6e2084f0b9" width="150%" height="400" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe></p>


Ouais, ouais, ton implémentation est meilleure, mais le fait est que tu peux maintenant manipuler les données JSON comme un objet Python normal !

Je ne sais pas pour toi, mais quand je relance le script de façon interactive, j'obtiens les résultats suivants :

------------------------------------------------------------------------------------

>>> s = "s" if len(users) > 1 else ""

>>> print(f"user{s} {max_users} completed {max_complete} TODOs")

users 5 and 10 completed 12 TODOs

-----------------------------------------------------------------------------------

C'est cool et tout, mais tu es ici pour en savoir plus sur JSON.

Pour ta tâche finale, tu créeras un fichier JSON qui contient les TODOs terminés pour chacun des utilisateurs qui ont complété le nombre maximum de TODOs.

Tout ce que tu as à faire est de filtrer les tâches à faire et d'écrire la liste résultante dans un fichier. Pour des raisons d'originalité, vous pouvez appeler le fichier de sortie filtered_data_file.json.

Il y a peut-être des moyens d'y arriver, mais en voici un :


----------------------------------------------------------------------------------------

# Define a function to filter out completed TODOs 

# of users with max completed TODOS.

def keep(todo):

    is_complete = todo["completed"]

    has_max_count = str(todo["userId"]) in users

    return is_complete and has_max_count


# Write filtered TODOs to file.

with open("filtered_data_file.json", "w") as data_file:

    filtered_todos = list(filter(keep, todos))

    json.dump(filtered_todos, data_file, indent=2)


--------------------------------------------------------------------------------

Parfait, tu t'es débarrassé de toutes les données dont tu n'as pas besoin et tu as sauvegardé les bonnes choses dans un tout nouveau fichier !

Exécutez à nouveau le script et consultez filtered_data_file.json pour vérifier que tout fonctionne. Il sera dans le même répertoire que scratch.py lorsque vous l'exécuterez.

Maintenant que tu es arrivé jusqu'ici, je parie que tu te sens comme un sacré canon, non ? Ne soyez pas arrogant : l'humilité est une vertu. Je suis cependant enclin à être d'accord avec vous.

Jusqu'à présent, la navigation s'est déroulée en douceur, mais tu devrais peut-être fermer les écoutilles pour cette dernière étape du voyage.

Encodage et décodage d'objets Python personnalisés

Que se passe-t-il lorsque nous essayons de sérialiser la classe Elf à partir de l'application Dungeons & Dragons sur laquelle vous travaillez ?

-----------------------------------------------------------------------------

class Elf:

    def __init__(self, level, ability_scores=None):

        self.level = level

        self.ability_scores = {

            "str": 11, "dex": 12, "con": 10,

            "int": 16, "wis": 14, "cha": 13

        } if ability_scores is None else ability_scores

        self.hp = 10 + self.ability_scores["con"]

--------------------------------------------------------------------------------

Comme on pouvait s'y attendre, Python se plaint que Elf n'est pas sérialisable (ce que vous sauriez si vous aviez déjà essayé de dire le contraire à un elfe) :

-------------------------------------------------

>>> elf = Elf(level=4)

>>> json.dumps(elf)

TypeError: Object of type 'Elf' is not JSON serializable

-----------------------------------------------


Bien que le module json puisse gérer la plupart des types Python intégrés, il ne comprend pas comment encoder des types de données personnalisés par défaut.

C'est comme si vous essayiez de placer une cheville carrée dans un trou rond - vous avez besoin d'une scie à buzz et d'une supervision parentale.

Simplification des structures de données

Maintenant, la question est de savoir comment traiter des structures de données plus complexes.

Eh bien, tu pourrais essayer d'encoder et de décoder le JSON à la main, mais il y a une solution un peu plus intelligente qui te fera économiser du travail.

 Au lieu de passer directement du type de données personnalisé à JSON, vous pouvez ajouter une étape intermédiaire.

Tout ce que tu as à faire est de représenter tes données en termes de types intégrés que json comprend déjà.

Essentiellement, vous traduisez l'objet le plus complexe en une représentation plus simple, que le module json traduit ensuite en JSON.

C'est comme la propriété transitive en mathématiques : si A = B et B = C, alors A = C.

Pour y arriver, vous aurez besoin d'un objet complexe avec lequel jouer.

Vous pouvez utiliser n'importe quelle classe personnalisée, mais Python a un type intégré appelé complex pour représenter des nombres complexes, et il n'est pas sérialisable par défaut.

Donc, pour le bien de ces exemples, votre objet complexe va être un objet complexe. Déjà confus ?

----------------------------------------------------------------------------------

>>> z = 3 + 8j

>>> type(z)

<class 'complex'>

>>> json.dumps(z)

TypeError: Object of type 'complex' is not JSON serializable

-----------------------------------------------------------------------------------

D'où viennent les nombres complexes ?

Tu vois, quand un nombre réel et un nombre imaginaire s'aiment beaucoup, ils s'additionnent pour produire un nombre qui est (à juste titre) appelé complexe.

Une bonne question à vous poser lorsque vous travaillez avec des types personnalisés est Quelle est la quantité minimale d'informations nécessaires pour recréer cet objet ?

Dans le cas de nombres complexes, vous n'avez besoin de connaître que les parties réelles et imaginaires, auxquelles vous pouvez accéder en tant qu'attributs sur l'objet complexe :

-----------------------------------

>>> z.real

3.0

>>> z.imag

8.0

-----------------------------------

Il suffit de passer les mêmes nombres dans un constructeur complexe pour satisfaire l'opérateur de comparaison __eq__ :

-------------------------------------

>>> complex(3, 8) == z

True

--------------------------------------

La décomposition des types de données personnalisées en leurs composants essentiels est essentielle aux processus de sérialisation et de désérialisation.

Encoding Custom Types

Pour traduire un objet personnalisé en JSON, il suffit de fournir une fonction d'encodage au paramètre par défaut de la méthode dump().

Le module json appellera cette fonction sur tous les objets qui ne sont pas sérialisables nativement. Voici une fonction de décodage simple que vous pouvez utiliser pour vous entraîner :

---------------------------------------------------------------------------

def encode_complex(z):

    if isinstance(z, complex):

        return (z.real, z.imag)

    else:

        type_name = z.__class__.__name__

        raise TypeError(f"Object of type '{type_name}' is not JSON serializable")

-----------------------------------------------------------------------------

Notes qu'on s'attend à ce que tu soulèves une erreur de type si tu n'obtiens pas le type d'objet auquel tu t'attendais.

De cette façon, tu évites de sérialiser accidentellement des Elfes. Maintenant tu peux essayer d'encoder des objets complexes pour toi-même !

---------------------------------------------------------------------

>>> json.dumps(9 + 5j, default=encode_complex)

'[9.0, 5.0]'

>>> json.dumps(elf, default=encode_complex)

TypeError: Object of type 'Elf' is not JSON serializable

-------------------------------------

Pourquoi avons-nous codé le nombre complexe comme un tuple ?

Excellente question ! Ce n'était certainement pas le seul choix, et ce n'est pas nécessairement le meilleur. En fait, ce ne serait pas une très bonne représentation si jamais vous vouliez décoder l'objet plus tard, comme vous le verrez bientôt.

L'autre approche courante consiste à sous-classer le JSONEncoder standard et à remplacer sa méthode par défaut() :

----------------------------------------------------------------------

class ComplexEncoder(json.JSONEncoder):

    def default(self, z):

        if isinstance(z, complex):

            return (z.real, z.imag)

        else:

            return super().default(z)

----------------------------------------------------------------------

Au lieu d'élever l'erreur de type vous-même, vous pouvez simplement laisser la classe de base s'en occuper.

 Vous pouvez l'utiliser soit directement dans la méthode dump() via le paramètre cls, soit en créant une instance de l'encodeur et en appelant sa méthode encodode() :

------------------------------------------------------------------------

>>> json.dumps(2 + 5j, cls=ComplexEncoder)

'[2.0, 5.0]'

>>> encoder = ComplexEncoder()

>>> encoder.encode(3 + 6j)

'[3.0, 6.0]'

-----------------------------------------------------------------------

Decoding Custom Types

Si les parties réelles et imaginaires d'un nombre complexe sont absolument nécessaires, elles ne sont en réalité pas suffisantes pour recréer l'objet.

C'est ce qui se produit lorsque vous essayez d'encoder un nombre complexe avec le ComplexEncoder puis de décoder le résultat :

-------------------------------------------------------------------------------------------------

>>> complex_json = json.dumps(4 + 17j, cls=ComplexEncoder)

>>> json.loads(complex_json)

[4.0, 17.0]

-------------------------------------------------------

Tout ce que tu obtiens en retour, c'est une liste, et tu devrais passer les valeurs dans un constructeur complexe si tu voulais à nouveau cet objet complexe.

Rappelle toi notre discussion sur la téléportation. Ce qui manque, ce sont les métadonnées ou les informations sur le type de données que vous encodez.

Je suppose que la question que tu devrais vraiment te poser est la suivante : Quelle est la quantité minimale d'informations qui est à la fois nécessaire et suffisante pour recréer cet objet ?

Le module json s'attend à ce que tous les types personnalisés soient exprimés en objets dans le standard JSON. Pour varier, tu peux créer un fichier JSON appelé complex_data.json et ajouter l'objet suivant représentant un nombre complexe :

---------------------------------------

{

    "__complex__": true,

    "real": 42,

    "imag": 36

}

----------------------------------------

Tu vois la partie intelligente ? Cette clé "__complex__" est la métadonnée dont nous venons de parler. La valeur associée n'a pas vraiment d'importance. Pour que ce petit bidouillage fonctionne, il vous suffit de vérifier que la clé existe :

------------------------------------------------------------------------

def decode_complex(dct):

    if "__complex__" in dct:

        return complex(dct["real"], dct["imag"])

    return dct

------------------------------------------------------------------------

Si "__complex__" n'est pas dans le dictionnaire, vous pouvez simplement retourner l'objet et laisser le décodeur par défaut s'en occuper.

Chaque fois que la méthode load() tente d'analyser un objet, vous avez la possibilité d'intercéder avant que le décodeur par défaut n'ait son chemin avec les données.

Vous pouvez le faire en passant votre fonction de décodage au paramètre object_hook.

Maintenant, jouez au même genre de jeu qu'avant :

------------------------------------------------------------------------------

>>> with open("complex_data.json") as complex_data:

...    data = complex_data.read()

...    z = json.loads(data, object_hook=decode_complex)

... 

>>> type(z)

<class 'complex'>

-------------------------------------------------------------------------------

Alors que object_hook peut sembler être l'équivalent du paramètre par défaut de la méthode dump(), l'analogie commence et se termine vraiment là.

Cela ne fonctionne pas non plus avec un seul objet.

Essayez de mettre cette liste de nombres complexes dans complex_data.json et de relancer le script :

-------------------------------------

[

  {

    "__complex__":true,

    "real":42,

    "imag":36

  },

  {

    "__complex__":true,

    "real":64,

    "imag":11

  }

]


----------------------------------------

Si tout se passe bien, vous obtiendrez une liste d'objets complexes :

---------------------------------------------------------------------------------

>>> with open("complex_data.json") as complex_data:

...    data = complex_data.read()

...    numbers = json.loads(data, object_hook=decode_complex)

... 

>>> numbers

[(42+36j), (64+11j)]

--------------------------------------------------------------------------------------------

Vous pouvez également essayer de sous-classer JSONDecoder et de remplacer object_hook, mais il est préférable de s'en tenir à la solution légère dans la mesure du possible.

Terminé !

Félicitations, vous pouvez maintenant utiliser le pouvoir puissant de JSON pour tous vos besoins infâmes en Python.

Bien que les exemples avec lesquels vous avez travaillé ici soient certainement inventés et trop simplistes, ils illustrent un flux de travail que vous pouvez appliquer à des tâches plus générales :

  1. Import du package json.
  2. Lire les données avec load() ou loads().
  3. Traiter les données.
  4. Ecrire les données modifiées avec dump() ou dumps().

Ce que vous faites de vos données une fois qu'elles ont été chargées en mémoire dépendra de votre cas d'utilisation.

En général, votre objectif sera de recueillir des données d'une source, d'extraire des informations utiles et de les transmettre ou de les conserver.

Tu as bien aimé l'article ou tu as mal aux yeux :

Le lien du PDF

Article écrit par :
Mikael Monjour
Data et Automatisation