Python et JSON forment une paire naturelle.
Que vous construisiez une API REST avec FastAPI
ou Django,
que vous traitiez des pipelines de données ou que vous lisiez simplement un fichier de configuration, vous travaillerez constamment avec du JSON.
La bonne nouvelle : la bibliothèque standard de Python contient tout ce dont vous avez besoin dans le
module json.
Aucun pip install requis.
Les quatre fonctions que vous utilisez vraiment
Le module json vous offre quatre fonctions pour le travail quotidien :
json.loads(str)— analyse une chaîne JSON en objet Pythonjson.dumps(obj)— convertit un objet Python en chaîne JSONjson.load(file)— analyse du JSON directement depuis un objet fichierjson.dump(obj, file)— écrit un objet Python en JSON dans un fichier
Le s dans loads / dumps signifie string (chaîne).
Ceux sans le s travaillent avec des objets fichier. Facile à retenir une fois que vous connaissez la règle.
json.loads() — Analyser une chaîne JSON
import json
json_string = '{"name": "Alice", "age": 30, "active": true, "score": 98.5}'
user = json.loads(json_string)
print(user["name"]) # Alice
print(user["age"]) # 30
print(user["active"]) # True
print(type(user)) # <class 'dict'>Remarquez le mappage de types : JSON true devient Python True,
JSON false devient Python False, JSON null devient Python None.
Les objets JSON deviennent des dict Python, les tableaux JSON deviennent des list Python.
json.dumps() — Sérialiser en chaîne JSON
import json
user = {
"name": "Bob",
"age": 25,
"roles": ["admin", "editor"],
"active": True,
"extra": None
}
# Compact (idéal pour la transmission réseau)
compact = json.dumps(user)
print(compact)
# {"name": "Bob", "age": 25, "roles": ["admin", "editor"], "active": true, "extra": null}
# Formaté (idéal pour les logs et la lecture humaine)
pretty = json.dumps(user, indent=2)
print(pretty)
# {
# "name": "Bob",
# "age": 25,
# "roles": [
# "admin",
# "editor"
# ],
# "active": true,
# "extra": null
# }Remarquez le mappage de types inverse : Python True → JSON true,
Python None → JSON null. Python gère cela automatiquement.
Lire du JSON depuis un fichier
C'est probablement le cas d'utilisation le plus courant — lire un fichier de configuration ou de données au démarrage :
import json
# Lire et analyser en une seule étape
with open("config.json", "r", encoding="utf-8") as f:
config = json.load(f)
print(config["database"]["host"]) # localhost
print(config["database"]["port"]) # 5432Spécifiez toujours encoding="utf-8" lors de l'ouverture de fichiers JSON. JSON est spécifié en UTF-8
par la RFC 8259,
et l'omettre peut causer des problèmes sous Windows où l'encodage par défaut est parfois cp1252.
Écrire du JSON dans un fichier
import json
results = {
"timestamp": "2024-01-15T09:30:00Z",
"total": 1523,
"processed": 1521,
"failed": 2,
"errors": [
{"id": 42, "reason": "missing field"},
{"id": 99, "reason": "invalid format"}
]
}
with open("results.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2)
print("Résultats sauvegardés dans results.json")Gérer les erreurs correctement
json.loads() lève une exception
json.JSONDecodeError
(sous-classe de ValueError) lorsque l'entrée n'est pas du JSON valide. Gérez-la toujours lors de l'analyse
de données que vous ne contrôlez pas :
import json
def safe_parse(json_str):
try:
return json.loads(json_str)
except json.JSONDecodeError as e:
print(f"JSON invalide à la ligne {e.lineno}, colonne {e.colno}: {e.msg}")
return None
data = safe_parse('{"name": "Alice"}') # fonctionne correctement
bad = safe_parse('not json at all') # affiche l'erreur, retourne None
also_bad = safe_parse('{"key": }') # affiche l'erreur avec la positionJSONDecodeError vous donne la ligne et la colonne exactes où l'analyse a échoué,
ce qui est utile lors du débogage de grands fichiers JSON.
Options utiles de dumps()
import json
data = {
"z_key": 1,
"a_key": 2,
"price": 9.999999999
}
# Trier les clés par ordre alphabétique (idéal pour une sortie reproductible / diffs)
print(json.dumps(data, sort_keys=True, indent=2))
# {
# "a_key": 2,
# "price": 9.999999999,
# "z_key": 1
# }
# S'assurer que les caractères non-ASCII sont préservés (par défaut : échappés en \uXXXX)
data2 = {"city": "Münich", "greeting": "こんにちは"}
print(json.dumps(data2, ensure_ascii=False))
# {"city": "Münich", "greeting": "こんにちは"}
# Avec ensure_ascii=True (par défaut) :
print(json.dumps(data2))
# {"city": "M\u00fcnich", "greeting": "\u3053\u3093\u306b\u3061\u306f"}ensure_ascii=False est une option que j'ajoute toujours lors de l'écriture de fichiers JSON contenant
du texte non-ASCII. La version échappée est techniquement du JSON valide, mais bien plus difficile à lire dans un éditeur de texte.
Sérialiser des objets personnalisés
Par défaut, json.dumps() ne peut pas sérialiser les instances de classes personnalisées ni les objets
datetime.
Vous avez deux options : créer une sous-classe de
json.JSONEncoder,
ou convertir en dict au préalable :
import json
from datetime import datetime, date
# Option 1 : classe d'encodeur personnalisée
class AppEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
return super().default(obj)
data = {"name": "Alice", "created_at": datetime(2024, 1, 15, 9, 30)}
print(json.dumps(data, cls=AppEncoder, indent=2))
# {
# "name": "Alice",
# "created_at": "2024-01-15T09:30:00"
# }
# Option 2 : paramètre default= (plus simple pour les conversions ponctuelles)
print(json.dumps(data, default=str, indent=2)) # convertit tout inconnu en strUn pattern pratique : chargement de fichier de configuration
Voici un pattern du monde réel que j'utilise dans presque tous mes projets Python — un chargeur de configuration qui lit un fichier JSON avec des valeurs par défaut raisonnables :
import json
import os
from pathlib import Path
DEFAULTS = {
"database": {"host": "localhost", "port": 5432},
"debug": False,
"log_level": "INFO"
}
def load_config(path="config.json"):
config = DEFAULTS.copy()
config_path = Path(path)
if config_path.exists():
with open(config_path, "r", encoding="utf-8") as f:
try:
user_config = json.load(f)
# Fusion profonde : les paramètres utilisateur remplacent les valeurs par défaut
for key, value in user_config.items():
if isinstance(value, dict) and key in config:
config[key].update(value)
else:
config[key] = value
except json.JSONDecodeError as e:
print(f"Avertissement : config.json est invalide ({e.msg}), utilisation des valeurs par défaut")
return config
config = load_config()
print(config["database"]["host"]) # localhost (ou valeur remplacée)Conclusion
Le module json de Python couvre tout ce dont vous avez besoin sans aucune dépendance.
Les règles clés : utilisez loads()/dumps() pour les chaînes, load()/dump()
pour les fichiers, gérez toujours JSONDecodeError lors de l'analyse de données externes, et ajoutez
ensure_ascii=False quand vos données contiennent des caractères non-latins.
Pour déboguer des données JSON, le Formateur JSON et le
Validateur JSON peuvent vous faire gagner beaucoup de temps.