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/google
Using 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.py
key : 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…).