JSON : load, dump et fichier en erreur
Passer d'un fichier JSON à des listes et dictionnaires Python et inversement, choisir entre load/dump et loads/dumps, et arrêter proprement le script avec sys.exit quand le fichier est inutilisable.
Mis à jour le
À recouper avec le cours. Cette leçon a été écrite à partir du plan du chapitre, avant relecture avec les supports. En cas de doute, le cours fait foi.
Pourquoi on voit ça
JSON est le format d’échange le plus courant en DevOps : inventaires, fichiers de configuration, réponses d’API. C’est du texte, mais structuré exactement comme les listes et dictionnaires que tu connais.
Le fil rouge lit son parc dans servers.json. Deux questions se posent : comment transformer ce texte en objets Python, et que faire quand le fichier est absent ou cassé ? Un outil d’administration ne doit pas répondre par un traceback de vingt lignes.
Dans toute la leçon, servers.json contient :
[
{"name": "web-1", "env": "prod", "port": 80, "up": true, "owner": null},
{"name": "db-1", "env": "dev", "port": 5432, "up": false, "owner": "zoe"}
]
JSON ↔ Python : les types
| JSON | Python |
|---|---|
objet {"name": "web-1"} |
dict |
tableau [80, 443] |
list |
chaîne "web-1" |
str |
nombre 80, 0.5 |
int, float |
true, false |
True, False |
null |
None |
Trois différences d’écriture à repérer : en JSON, true, false et null sont en minuscules, et les chaînes sont toujours entre guillemets doubles.
load et dump : avec un fichier
json.load(f) lit un fichier ouvert et renvoie l’objet Python.
import json
with open("servers.json", encoding="utf-8") as f:
servers = json.load(f)
print(type(servers))
print(servers[0]["name"], servers[0]["port"] + 1)
print(servers[1]["up"], servers[0]["owner"])
<class 'list'>
web-1 81
False None
Le résultat est une vraie liste de dictionnaires. port est un vrai int : on peut calculer avec.
json.dump(data, f) fait l’inverse : il écrit un objet Python dans un fichier ouvert en écriture.
with open("report.json", "w", encoding="utf-8") as f:
json.dump({"name": "web-1", "port": 80}, f)
loads et dumps : avec une chaîne
Le s final veut dire string. Ces deux fonctions ne touchent à aucun fichier.
| Fonction | Entrée | Sortie |
|---|---|---|
json.load(f) |
fichier ouvert | objet Python |
json.loads(text) |
chaîne | objet Python |
json.dump(data, f) |
objet Python + fichier ouvert | rien (écrit dans le fichier) |
json.dumps(data) |
objet Python | chaîne |
s = json.loads('{"name": "web-1", "up": true, "owner": null}')
print(s)
print(json.dumps({"name": "web-1", "up": True, "owner": None}))
{'name': 'web-1', 'up': True, 'owner': None}
{"name": "web-1", "up": true, "owner": null}
La première ligne est l’affichage d’un dict Python. La deuxième est du texte JSON.
Le piège classique : json.load("servers.json"). load attend un fichier ouvert, pas un nom : tu obtiens AttributeError: 'str' object has no attribute 'read'.
indent et ensure_ascii=False
Par défaut, dumps et dump écrivent tout sur une ligne et remplacent les accents par des codes.
print(json.dumps({"env": "préprod"}))
print(json.dumps({"env": "préprod"}, ensure_ascii=False))
print(json.dumps({"name": "web-1", "port": 80}, indent=2))
{"env": "pr\u00e9prod"}
{"env": "préprod"}
{
"name": "web-1",
"port": 80
}
indent=2: une valeur par ligne, décalée de 2 espaces. Pour un fichier que des humains vont relire.ensure_ascii=False: les accents restent tels quels. Pense alors àencoding="utf-8"dans leopen.
Les dates : isoformat()
JSON n’a pas de type date. Donner un objet datetime à dumps lève une erreur :
TypeError: Object of type datetime is not JSON serializable
La solution : transformer la date en chaîne avec isoformat().
from datetime import datetime
d = datetime(2026, 11, 14, 9, 30)
print(json.dumps({"name": "web-1", "checked": d.isoformat()}))
{"name": "web-1", "checked": "2026-11-14T09:30:00"}
Le format ISO (année-mois-jour, T, heure) est le même partout et se trie correctement.
Fichier en erreur : l’erreur fatale
Deux choses peuvent rendre servers.json inutilisable :
- il n’existe pas :
FileNotFoundError; - son contenu n’est pas du JSON valide :
json.JSONDecodeError.
Dans les deux cas, impossible de continuer : sans inventaire, il n’y a rien à vérifier. C’est une erreur fatale. On affiche un message court et on arrête le script en signalant l’échec.
import json
import sys
try:
with open("servers.json", encoding="utf-8") as f:
servers = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
print("Erreur fatale :", e)
sys.exit(1)
print(len(servers), "serveurs")
Avec un fichier absent :
Erreur fatale : [Errno 2] No such file or directory: 'servers.json'
except (A, B)
Pour traiter plusieurs exceptions de la même façon, mets-les dans un tuple, entre parenthèses. Avec as e, e contient l’exception qui s’est produite, quelle qu’elle soit.
sys.exit et $?
sys.exit(n) arrête le script tout de suite et donne au shell le code de sortie n. La convention : 0 = tout va bien, autre chose = échec. Dans le shell, $? contient le code de la dernière commande.
python3 fleetcheck.py
echo $?
Erreur fatale : [Errno 2] No such file or directory: 'servers.json'
1
C’est ce code qu’un script shell ou une chaîne d’intégration continue regarde pour savoir si l’étape a réussi. Un script qui se termine normalement sort avec 0.
SystemExit
sys.exit n’a rien de magique : il lève l’exception SystemExit. Deux conséquences :
- un bloc
finallys’exécute quand même avant l’arrêt ; SystemExitn’est pas un enfant deException. Unexcept Exceptionne l’attrape donc pas, et le script s’arrête bien.
import sys
try:
sys.exit(2)
except Exception:
print("attrapé")
finally:
print("fin")
print("suite")
fin
Le code de sortie est 2. Ni attrapé ni suite ne s’affichent.
À retenir
- JSON → Python : objet =
dict, tableau =list,true/false=True/False,null=None. load/dumptravaillent avec un fichier ouvert ;loads/dumpsavec une chaîne.indent=2pour un JSON lisible,ensure_ascii=Falsepour garder les accents.- Pas de date en JSON : on passe par
isoformat(). - Erreur fatale :
except (FileNotFoundError, json.JSONDecodeError) as e:puis message etsys.exit(1). sys.exit(n)lèveSystemExit; le shell lit le code avececho $?(0 = succès).
Entraînement
12 questions, dont 7 à compléter. Tu réponds, on passe à la suivante. La note arrive à la fin.
Ta note
Cartes · 8
Réviser les cartes ici
Pourquoi