Introduction
Les fonctionnalités essentielles à aborder lorsqu’on souhaite apprendre et utiliser très rapidement Python :
- Python - Comprendre et démystifier virtualenv (Publication : Janvier 2020)
- Lire et écrire des fichiers JSON, traiter des données JSON avec Python (Publication : Avril 2020)
- Manipuler les arguments d’un programme Python avec les packages argparse et getopt (Publication : Avril 2020)
- Configuration applicative : variables d’environnement, fichiers ini et YAML (Publication : Avril 2020)
- Gestion des requêtes HTTP avec les packages requests et httplib2 (Publication : Avril 2020)
Dans ce chapître, comment lire et écrire des données JSON dans un programme Python avec le package système json.
L’environnement Python
L’environnement python est le suivant, il est sourcé avec le fichier $HOME/.python-3.8:
$HOME/.python-3.8
#!/bin/bash export PYHOME=/opt/python/python-3.8 export PATH=$PYHOME/bin:$PATH export LD_LIBRARY_PATH=$PYHOME/lib:$LD_LIBRARY_PATH export PYTHONPATH=/opt/python/packages sqlpac@vpsfrsqlpac2$ . $HOME/.python-3.8 sqlpac@vpsfrsqlpac2$ which python3 sqlpac@vpsfrsqlpac2$ which pip3/opt/python/python-3.8/bin/python3 /opt/python/python-3.8/bin/pip3
virtualenv est installé et un environnement virtuel isolé est mis en place pour le projet :
sqlpac@vpsfrsqlpac2$ cd /home/sqlpac sqlpac@vpsfrsqlpac2$ virtualenv /home/sqlpac/googleUsing base prefix '/opt/python/python-3.8' New python executable in /home/sqlpac/google/bin/python3.8 Also creating executable in /home/sqlpac/google/bin/python Installing setuptools, pip, wheel... done.sqlpac@vpsfrsqlpac2$ source /home/sqlpac/google/bin/activate(google) sqlpac@vpsfrsqlpac2:/home/sqlpac$
Les données JSON de l’exemple
On connaît le format JSON renvoyé par les API Google Indexing lorsqu’on requête le statut d’indexation pour une URL donnée :
{
"url": "https://www.sqlpac.com/referentiel/docs/mariadb-columnstore-1.2.3-installation-standalone-ubuntu.html",
"latestUpdate":
{ "url": "https://www.sqlpac.com/referentiel/docs/mariadb-columnstore-1.2.3-installation-standalone-ubuntu.html",
"type": "URL_UPDATED",
"notifyTime": "2020-04-10T17:43:21.198591915Z"
}
}
La variable d’environnement $PRJ définit le répertoire de travail
(google) sqlpac@vpsfrsqlpac2:/home/sqlpac$ mkdir google/json (google) sqlpac@vpsfrsqlpac2:/home/sqlpac$ export PRJ=/home/sqlpac/google/json (google) sqlpac@vpsfrsqlpac2:/home/sqlpac$ cd $PRJ(google) sqlpac@vpsfrsqlpac2:/home/sqlpac/google/json$
Voyons comment manipuler JSON dans un programme Python.
Le package système json
Le package système json est disponible en natif, il n’y a qu’à l’importer :
$PRJ/handling-json.py
import json
C’est fini !
Lecture des données JSON
La méthode loads : chargement depuis une variable string
Utiliser la méthode loads pour charger des données JSON depuis une variable string :
import json
response_json='{"a":1, "b":2}'
loaded_json = json.loads(response_json)
for key in loaded_json:
print("key : %s, value: %s" % (key,loaded_json[key]))
(google) sqlpac@vpsfrsqlpac2:/home/sqlpac/google/json$ python3 handling-json.pykey : a, value: 1 key : b, value: 2
Dans la vraie vie, les données JSON ne sont pas définies avec un string sur une seule ligne , pour définir un string sur de multiples lignes :
import json
response_json = '''{
"url": "https://www.sqlpac.com/referentiel/docs/mariadb-columnstore-1.2.3-installation-standalone-ubuntu.html",
"latestUpdate":
{ "url": "https://www.sqlpac.com/referentiel/docs/mariadb-columnstore-1.2.3-installation-standalone-ubuntu.html",
"type": "URL_UPDATED",
"notifyTime": "2020-04-10T17:43:21.198591915Z"
},
"isactive" : true,
"floatvalue" : 1.2399,
"intvalue" : 1,
"ostypes" : ["linux","macos","windows"]
}'''
loaded_json = json.loads(response_json)
for key in loaded_json:
print("%s %s %s" % (key, type(loaded_json[key]), loaded_json[key]))
Des données supplémentaires sont ajoutées dans l’exemple JSON pour la démo : isactive, floatvalue, intvalue, ostypes.
Les types de données sont également affichés avec la fonction type(). Les types de données sont alors les suivants :
| Key | Type | Valeur |
|---|---|---|
| url | <class 'str'> | https://www.sqlpac.com/ref… |
| latestUpdate | <class 'dict'> | {'url': 'https://www.sqlpac.com/…', 'type': 'URL_UPDATED'…} |
| isactive | <class 'bool'> | True |
| floatvalue | <class 'float'> | 1.2300 |
| intvalue | <class 'int'> | 1 |
| ostypes | <class 'list'> | ["linux","macos","windows"] |
Lorsqu’on est habitués à Javascript, la translation pour le type de données est la suivante :
| Javascript | Python | |
|---|---|---|
| Object | dict | |
| Array | list | |
| String | str | |
| Number (int) | int | |
| Number (float) | float | |
| true | false | True | False |
print(loaded_json["url"])https://www.sqlpac.com/referentiel…
Alors naturellement, on essaie de manipuler les données obtenues avec la syntaxe Javascript "dot notation", mais cela ne fonctionne pas :
print(loaded_json.url)Traceback (most recent call last): File "handling-json.py", line 23, in <module> print(loaded_json.url) AttributeError: 'dict' object has no attribute 'url'
Pour utiliser la notation "dot", une classe doit être créée et associée :
import json response_json = '''{ "url": "https://www.sqlpac.com/referentiel/docs/mariadb-columnstore-1.2.3-installation-standalone-ubuntu.html", "latestUpdate": { "url": "https://www.sqlpac.com/referentiel/docs/mariadb-columnstore-1.2.3-installation-standalone-ubuntu.html", "type": "URL_UPDATED", "notifyTime": "2020-04-10T17:43:21.198591915Z" }, "isactive" : true, "floatvalue" : 1.2399, "intvalue" : 1, "ostypes" : ["linux","macos","windows"] }''' class google(): def __init__(self, data): self.__dict__ = json.loads(data) google_answer = google(response_json) print(google_answer.url) print(google_answer.latestUpdate["type"])https://www.sqlpac.com/referentiel… URL_UPDATED
Comme attendu, google_answer.latestUpdate.type n’est pas disponible comme cela serait le cas avec Javascript, mais google_answer.latestUpdate["type"].
Python n’est pas Javascript, on doit parfois quitter ses habitudes de programmation.
La méthode load : chargement depuis un fichier
Quand les données JSON sont dans un fichier, la méthode load est utilisée :
import json with open('json-data.json', 'r') as f: json_dict = json.load(f) print(json_dict["url"])https://www.sqlpac.com/referentiel…
Pas de différence par rapport à l’exemple précédent, pour utiliser la notation dot, créer une classe :
import json class google(): def __init__(self, filename): with open(filename, 'r') as f: self.__dict__ = json.load(f) google_answer = google('json-data.json') print(google_answer.url)https://www.sqlpac.com/referentiel…
Gestion des données JSON mal formatées
Utiliser les blocs try / except pour gérer les exceptions rencontrées lors du chargement de données JSON mal formatées :
import json with open('json-data.json') as f: try: data = json.load(f) except Exception as e: print("Exception raised | %s " % str(e)) exit() print(data["url"])Exception raised | Expecting ',' delimiter: line 6 column 5 (char 306)
Doublons Clé/valeur
Qu’en est-il si une paire clé/valeur est définie plus d’une fois :
{
"url": "1.html",
"url": 1
}
Pas d’exception levée, la valeur et le type de données sont ceux de la dernière paire clé/valeur lue dans les données JSON :
import json … print("value : %s, data type : %s" % (data["url"], type(data["url"]) ))value : 18, data type : <class 'int'>
Retourner et écrire des données JSON
Imaginons que l’on souhaite retourner la réponse factice ci-dessous :
{
"url": "https://www.sqlpac.com/archives/2020",
"ostypes": [ "linux", "macos","windows"],
"isactive": true,
"price": "12€",
"details": {
"returncode": "0",
"reason": "none"
}
}
La méthode dumps
La méthode dumps retourne un string JSON à partir du dictionnaire Python :
import json response = {} response["url"] = "https://www.sqlpac.com/archives/2020" response["ostypes"] = ["linux","macos","windows"] response["isactive"] = True response["price"] = "12$" response["details"] = { "returncode": 1, "reason":"none" } str_response = json.dumps(response) print(str_response){"url": "https://www.sqlpac.com/archives/2020", "ostypes": ["linux", "macos", "windows"], "isactive": true, "price": "12$", "details": {"returncode": 1, "reason": "none"}}
Les données sont bien transtypées dans le sens du retour :
| Python | Javascript | |
|---|---|---|
| dict | Object | |
| list | Array | |
| str | String | |
| int | Number (int) | |
| float | Number (float) | |
| True | False | true | false |
"Human readable"
Les données sont retournées sur une ligne unique, utiliser l’option d’indentation indent pour obtenir
un format plus lisible par un humain.
str_response = json.dumps(response, indent=4)
print(str_response)
{
"url": "https://www.sqlpac.com/archives/2020",
"ostypes": [
"linux",
"macos",
"windows"
],
"isactive": true,
"price": "12$",
"details": {
"returncode": 1,
"reason": "none"
}
}
Unicode
Et si il y a un caractère Unicode, par exemple 12€ au lieu de 12$. Le résultat va ressembler à ceci :
…
"price": "12\u20ac",
…
Par défaut, json.dumps garantit que le texte est encodé ASCII, si ce n’est pas le cas le texte est échappé. Appliquer l’option
ensure_ascii à False pour s’assurer que les caractères unicode ne sont pas retouchés :
str_response = json.dumps(response, indent=4, ensure_ascii=False)
print(str_response)
…
"price": "12€",
…
Trier les clés
L’ordre des clés n’est pas garanti ou prédéfini, pour forcer un ordre des clés, appliquer l’option sort_keys à True :
str_response = json.dumps(response, indent=4, ensure_ascii=False, sort_keys=True)
print(str_response)
{
"details": {
"reason": "none",
"returncode": 1
},
"isactive": true,
"ostypes": [
"linux",
"macos",
"windows"
],
"price": "12€",
"url": "https://www.sqlpac.com/archives/2020"
}
La méthode dump
Utiliser la méthode dump pour écrire les données JSON dans un fichier,
toutes les options décrites ci-dessus avec la méthode dumps
sont disponibles avec la méthode dump :
with open('response.json', 'w') as f:
json.dump(response,f,indent=4, ensure_ascii=False, sort_keys=False )
$PRJ/response.json
{
"url": "https://www.sqlpac.com/archives/2020",
"ostypes": [
"linux",
"macos",
"windows"
],
"isactive": true,
"price": "12€",
"details": {
"returncode": 1,
"reason": "none"
}
}
Conclusion
Sérialiser ou désérialiser des données JSON est très simple mais on doit oublier nos habitudes Javascript lorsqu’on charge des données JSON avec des programmes Python (dot notation…).