JSON vers CSV Python — Exemples DictWriter + pandas

·Backend Developer·Révisé parPriya Sharma·Publié

Utilisez le JSON vers CSV gratuit directement dans votre navigateur — sans installation.

Essayer JSON vers CSV en ligne →

Presque chaque pipeline de données finit par atteindre la même étape : une API retourne du JSON, mais le prochain consommateur — une feuille de calcul, un script d'import, une commande Redshift COPY — a besoin de CSV. Convertir JSON en CSV en Pythonsemble trivial jusqu'à ce que vous rencontriez des objets imbriqués, des clés incohérentes ou des valeurs datetime nécessitant un traitement spécial. Python vous offre deux approches solides : les modules intégrés json + csv pour les scripts sans dépendance, et pandas pour l'aplatissement des structures imbriquées et les jeux de données plus volumineux — ou le convertisseur JSON vers CSV en ligne pour des conversions ponctuelles rapides sans aucun code. Ce guide couvre les deux approches de bout en bout, avec des exemples Python 3.8+ exécutables.

  • csv.DictWriter convertit une liste de dicts en CSV sans dépendances — utilisez json.load() pour analyser, puis writeheader() + writerows().
  • Ouvrez toujours les fichiers CSV avec newline="" sous Windows pour éviter les lignes vides entre les lignes de données.
  • pd.json_normalize() aplatit le JSON imbriqué en un DataFrame plat avant d'appeler to_csv() — gère automatiquement l'imbrication multi-niveaux.
  • Passez index=False à DataFrame.to_csv() — sans cela, pandas écrit une colonne de numéros de ligne indésirable.
  • Pour les fichiers de plus de 500 Mo, utilisez ijson pour l'analyse JSON en streaming combinée avec csv.DictWriter pour une utilisation mémoire constante.

Qu'est-ce que la conversion JSON vers CSV ?

La conversion JSON vers CSV transforme un tableau d'objets JSON en format tabulaire où chaque objet devient une ligne et chaque clé devient un en-tête de colonne. JSON est hiérarchique — les objets peuvent s'imbriquer arbitrairement en profondeur. CSV est plat — chaque valeur se trouve dans une grille ligne-colonne. La conversion fonctionne parfaitement lorsque chaque objet partage le même ensemble de clés de premier niveau. Les objets imbriqués, les tableaux et les clés incohérentes sont là où les choses deviennent intéressantes. Les données brutes restent identiques ; seule la structure change.

Before · json
After · json
[{"order_id":"ord_91a3","total":149.99,"status":"shipped"},
 {"order_id":"ord_b7f2","total":34.50,"status":"pending"}]
order_id,total,status
ord_91a3,149.99,shipped
ord_b7f2,34.50,pending

csv.DictWriter — Convertir JSON en CSV sans Pandas

Le module csv est livré avec chaque installation Python. Pas de pip install, pas de gymnastique avec les environnements virtuels. csv.DictWriter prend une liste de dictionnaires et écrit chacun comme une ligne CSV, associant les clés du dict aux en-têtes de colonnes. Le paramètre fieldnames contrôle à la fois l'ordre des colonnes et les clés incluses.

Python 3.8+ — exemple minimal json vers csv
import json
import csv

# Exemples de données JSON — un tableau d'objets de commandes
json_string = """
[
  {"order_id": "ord_91a3", "product": "Wireless Keyboard", "quantity": 2, "unit_price": 74.99},
  {"order_id": "ord_b7f2", "product": "USB-C Hub", "quantity": 1, "unit_price": 34.50},
  {"order_id": "ord_c4e8", "product": "Monitor Stand", "quantity": 3, "unit_price": 29.95}
]
"""

records = json.loads(json_string)

with open("orders.csv", "w", newline="", encoding="utf-8") as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=records[0].keys())
    writer.writeheader()
    writer.writerows(records)

# orders.csv :
# order_id,product,quantity,unit_price
# ord_91a3,Wireless Keyboard,2,74.99
# ord_b7f2,USB-C Hub,1,34.50
# ord_c4e8,Monitor Stand,3,29.95

L'argument newline="" sur open()n'est pas facultatif sous Windows. Sans lui, vous obtenez des doubles retours chariot — qui apparaissent comme des lignes vides entre chaque ligne de données dans Excel. Sur macOS et Linux c'est inoffensif, donc incluez-le systématiquement.

Le code ci-dessus utilise json.loads() pour une chaîne. Utilisez json.load() (sans le sfinal) lors de la lecture depuis un handle de fichier. C'est une confusion très fréquente — l'un lit une chaîne, l'autre lit un objet fichier.

Python 3.8+ — lire un fichier JSON, écrire un fichier CSV
import json
import csv

with open("server_metrics.json", encoding="utf-8") as jf:
    metrics = json.load(jf)  # json.load() pour les objets fichier

# Les fieldnames explicites contrôlent l'ordre des colonnes
columns = ["timestamp", "hostname", "cpu_percent", "memory_mb", "disk_io_ops"]

with open("server_metrics.csv", "w", newline="", encoding="utf-8") as cf:
    writer = csv.DictWriter(cf, fieldnames=columns, extrasaction="ignore")
    writer.writeheader()
    writer.writerows(metrics)

# Seules les cinq colonnes spécifiées apparaissent, exactement dans cet ordre

Définir extrasaction="ignore" supprime silencieusement les clés des dicts qui ne figurent pas dans votre liste fieldnames. La valeur par défaut est "raise", qui lève une ValueError si un dict possède une clé inattendue. Choisissez celui qui correspond à votre tolérance aux surprises.

Note :csv.DictWriter vs csv.writer : DictWriter associe automatiquement les clés du dict aux positions de colonnes. csv.writerécrit des listes brutes comme lignes — vous gérez vous-même l'ordre des colonnes. DictWriter est presque toujours le bon choix pour la conversion JSON-vers-CSV car les enregistrements JSON sont déjà des dictionnaires.

Le module csv de Python est livré avec trois dialectes nommés : excel (délimiteur virgule, fins de ligne CRLF — le défaut), excel-tab (délimiteur tabulation, fins de ligne CRLF), et unix (fins de ligne LF, met entre guillemets tous les champs non numériques). Passez le nom du dialecte comme argument dialect à csv.DictWriter. Vous pouvez aussi définir un dialecte personnalisé avec csv.register_dialect() lorsque votre système cible a des règles inhabituelles de guillemets ou de délimiteurs. Pour la plupart des workflows JSON-vers-CSV, le dialecte excel est correct, mais passez à unix lorsque vous écrivez des fichiers qui seront traités par des outils POSIX comme awk ou sort.

Gestion des types non standards : datetime, UUID et Decimal

Le JSON des APIs contient souvent des dates sous forme de chaînes ISO, des UUIDs sous forme de chaînes avec tirets, et des valeurs monétaires sous forme de flottants. Lorsque vous analysez ces données en objets Python pour les traiter avant d'écrire le CSV, vous devez les reconvertir en chaînes. Le module csv appelle str() sur chaque valeur, donc la plupart des types fonctionnent simplement. Mais les objets datetime produisent des représentations textuelles par défaut désordonnées, et les valeurs Decimal nécessitent un formatage explicite pour éviter la notation scientifique.

Python 3.8+ — pré-traiter datetime et Decimal avant l'écriture CSV
import json
import csv
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID

# Simulation d'une réponse API analysée avec des types Python
transactions = [
    {
        "txn_id": UUID("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
        "created_at": datetime(2026, 3, 15, 9, 30, 0, tzinfo=timezone.utc),
        "amount": Decimal("1249.99"),
        "currency": "USD",
        "merchant": "CloudHost Inc.",
    },
    {
        "txn_id": UUID("b2c3d4e5-f6a7-8901-bcde-f12345678901"),
        "created_at": datetime(2026, 3, 15, 14, 12, 0, tzinfo=timezone.utc),
        "amount": Decimal("87.50"),
        "currency": "EUR",
        "merchant": "DataSync GmbH",
    },
]

def prepare_row(record: dict) -> dict:
    """Convertit les types non-chaînes en chaînes compatibles CSV."""
    return {
        "txn_id": str(record["txn_id"]),
        "created_at": record["created_at"].isoformat(),
        "amount": f"{record['amount']:.2f}",
        "currency": record["currency"],
        "merchant": record["merchant"],
    }

with open("transactions.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["txn_id", "created_at", "amount", "currency", "merchant"])
    writer.writeheader()
    for txn in transactions:
        writer.writerow(prepare_row(txn))

# transactions.csv :
# txn_id,created_at,amount,currency,merchant
# a1b2c3d4-e5f6-7890-abcd-ef1234567890,2026-03-15T09:30:00+00:00,1249.99,USD,CloudHost Inc.
# b2c3d4e5-f6a7-8901-bcde-f12345678901,2026-03-15T14:12:00+00:00,87.50,EUR,DataSync GmbH

La fonction prepare_row() est la bonne approche ici. Plutôt que d'essayer d'apprendre à csv.DictWriter les types personnalisés, vous normalisez chaque enregistrement en chaînes avant d'écrire. Je préfère appeler .isoformat() explicitement sur les objets datetime plutôt que de compter sur str() — le format de sortie est plus prévisible, et les parseurs en aval gèrent ISO 8601 de manière fiable.

Avertissement :Si vous laissez passer les valeurs Decimalsans formatage, les nombres très petits ou très grands peuvent s'afficher en notation scientifique (ex. : 1.5E+7). Formatez toujours Decimal avec une f-string explicite comme f"{value:.2f}" lors de l'écriture de données financières en CSV.

Une approche alternative pour les pipelines avec de nombreux types personnalisés est d'étendre json.JSONEncoder. Sous-classez-le, redéfinissez la méthode default() pour retourner une valeur sérialisable en JSON pour chaque type personnalisé, puis passez la sous-classe comme argument cls à json.dumps(). Ré-encoder via l'encodeur personnalisé avant d'écrire en CSV normalise tous les types en une seule étape sans appel prepare_row() par ligne. Le modèle prepare_row() montré ci-dessus est plus simple pour les scripts ponctuels ; l'approche par sous-classe JSONEncoder passe mieux à l'échelle lorsque le même modèle de domaine avec des types personnalisés est partagé entre de nombreuses étapes de pipeline ou microservices.

Référence des paramètres csv.DictWriter

La signature complète du constructeur est csv.DictWriter(f, fieldnames, restval="", extrasaction="raise", dialect="excel", **fmtparams). La plupart ont des valeurs par défaut sensées. Ceux que vous changerez réellement sont fieldnames, delimiter, et extrasaction.

Paramètre
Type
Défaut
Description
f
file object
(requis)
Tout objet avec une méthode write() — généralement issu de open()
fieldnames
sequence
(requis)
Liste de clés définissant l'ordre des colonnes dans le CSV
restval
str
""
Valeur écrite lorsqu'un dict ne contient pas une clé de fieldnames
extrasaction
str
"raise"
"raise" lève ValueError pour les clés inattendues ; "ignore" les supprime silencieusement
dialect
str / Dialect
"excel"
Règles de formatage prédéfinies — "excel", "excel-tab" ou "unix"
delimiter
str
","
Caractère unique séparant les champs — utiliser "\t" pour la sortie TSV
quotechar
str
"
Caractère utilisé pour encadrer les champs contenant le délimiteur
quoting
int
csv.QUOTE_MINIMAL
Contrôle quand les guillemets sont appliqués — MINIMAL, ALL, NONNUMERIC, NONE
lineterminator
str
"\r\n"
Chaîne ajoutée après chaque ligne — remplacer par "\n" pour une sortie style Unix

pandas — Convertir JSON en CSV avec les DataFrames

Si vous travaillez déjà dans une base de code fortement axée sur pandas, ou si votre JSON a des objets imbriqués que vous devez aplatir, l'approche pandas représente bien moins de code que la version stdlib. La contrepartie : pandas est une dépendance de ~30 Mo. Pour un script jetable, c'est acceptable. Pour une image Docker que vous déployez en production, l'approche stdlib garde les choses plus légères.

Python 3.8+ — pandas read_json puis to_csv
import pandas as pd

# Lire le tableau JSON directement dans un DataFrame
df = pd.read_json("warehouse_inventory.json")

# Écrire en CSV — index=False empêche les numéros de ligne auto-générés
df.to_csv("warehouse_inventory.csv", index=False)

# C'est tout. Deux lignes. pandas infère les types de colonnes automatiquement.

L'option index=False est une de ces choses qu'on recherche à chaque fois. Sans elle, pandas écrit une colonne 0, 1, 2, ... comme première colonne de votre CSV. Personne ne veut ça.

Aplatissement du JSON imbriqué avec json_normalize

Les réponses d'API réelles sont rarement plates. Les commandes contiennent des adresses de livraison, les utilisateurs contiennent des préférences imbriquées, les événements de télémétrie contiennent des métadonnées imbriquées. pd.json_normalize() parcourt les dictionnaires imbriqués et les aplatit en colonnes avec des noms séparés par des points.

Python 3.8+ — aplatir le JSON imbriqué avec json_normalize
import json
import pandas as pd

api_response = """
[
  {
    "order_id": "ord_91a3",
    "placed_at": "2026-03-15T09:30:00Z",
    "customer": {
      "name": "Sarah Chen",
      "email": "s.chen@example.com",
      "tier": "premium"
    },
    "shipping": {
      "method": "express",
      "address": {
        "city": "Portland",
        "state": "OR",
        "zip": "97201"
      }
    },
    "total": 299.95
  },
  {
    "order_id": "ord_b7f2",
    "placed_at": "2026-03-15T14:12:00Z",
    "customer": {
      "name": "James Park",
      "email": "j.park@example.com",
      "tier": "standard"
    },
    "shipping": {
      "method": "standard",
      "address": {
        "city": "Austin",
        "state": "TX",
        "zip": "73301"
      }
    },
    "total": 87.50
  }
]
"""

orders = json.loads(api_response)

# json_normalize aplatit les dicts imbriqués — sep contrôle le délimiteur
df = pd.json_normalize(orders, sep="_")
df.to_csv("flat_orders.csv", index=False)

# Colonnes résultantes :
# order_id, placed_at, customer_name, customer_email, customer_tier,
# shipping_method, shipping_address_city, shipping_address_state,
# shipping_address_zip, total

Le paramètre sep="_" contrôle comment les noms de clés imbriquées sont joints. La valeur par défaut est ".", qui produit des colonnes comme customer.name. Je préfère les tirets bas car les points dans les noms de colonnes causent des problèmes avec les imports SQL et certaines formules de tableur.

Pour les réponses d'API qui enveloppent le tableau d'enregistrements sous une clé imbriquée, utilisez le paramètre record_path. Si la réponse ressemble à {"data": {"orders": [...]}}, passez record_path=["data", "orders"] pour naviguer jusqu'à la bonne liste. Le paramètre optionnel meta vous permet de récupérer des champs du niveau parent aux côtés des enregistrements imbriqués — utile quand la réponse inclut des informations de pagination de premier niveau (numéro de page, nombre total) que vous souhaitez avoir comme colonne dans chaque ligne. Ensemble, record_path et meta gèrent la plupart des formes de réponses d'API imbriquées du monde réel sans prétraitement personnalisé.

Référence des paramètres DataFrame.to_csv()

DataFrame.to_csv() possède plus de 20 paramètres. Voici ceux qui comptent pour les workflows JSON-vers-CSV.

Paramètre
Type
Défaut
Description
path_or_buf
str / Path / None
None
Chemin de fichier ou buffer — None retourne le CSV sous forme de chaîne
sep
str
","
Délimiteur de champ — utiliser "\t" pour TSV
index
bool
True
Écrire l'index de ligne en première colonne — presque toujours régler à False
columns
list
None
Sous-ensemble et réordonnancement des colonnes en sortie
header
bool / list
True
Écrire les noms de colonnes — mettre False lors de l'ajout à un fichier existant
encoding
str
"utf-8"
Encodage de sortie — utiliser "utf-8-sig" pour la compatibilité Excel sous Windows
na_rep
str
""
Représentation textuelle des valeurs manquantes (NaN, None)
quoting
int
csv.QUOTE_MINIMAL
Contrôle quand les champs sont mis entre guillemets
Python 3.8+ — to_csv avec les surcharges de paramètres courants
import pandas as pd

df = pd.read_json("telemetry_events.json")

# Sortie TSV avec encodage explicite et gestion des valeurs manquantes
df.to_csv(
    "telemetry_events.tsv",
    sep="\t",
    index=False,
    encoding="utf-8",
    na_rep="NULL",
    columns=["event_id", "timestamp", "source", "severity", "message"],
)

# Écrire sur stdout pour redirection dans des scripts shell
print(df.to_csv(index=False))

# Retourner comme chaîne (aucun fichier écrit)
csv_string = df.to_csv(index=False)
print(len(csv_string), "caractères")

Convertir JSON en CSV depuis un fichier et une réponse API

Les deux scénarios les plus courants dans la vraie vie : lire du JSON depuis un fichier sur disque et le convertir, ou récupérer du JSON depuis une API HTTP et sauvegarder le résultat en CSV. En développement, vous pouvez vous passer de gestion des erreurs. En production, ce choix devient une alerte à 2h du matin. Les fichiers pourraient ne pas exister, les APIs pourraient retourner des codes 4xx ou 5xx au lieu de JSON, le corps de la réponse pourrait être un objet d'erreur plutôt qu'un tableau, ou le JSON pourrait être tronqué à cause d'un timeout réseau. Les modèles ci-dessous gèrent tous ces cas explicitement, journalisent les erreurs sur stderr, et retournent un nombre de lignes pour que les appelants puissent détecter les sorties à zéro ligne et alerter en conséquence.

Fichier sur disque — Lire, Convertir, Sauvegarder

Python 3.8+ — convertir un fichier JSON en CSV avec gestion des erreurs
import json
import csv
import sys

def json_file_to_csv(input_path: str, output_path: str) -> int:
    """Convertit un fichier JSON contenant un tableau d'objets en CSV.
    Retourne le nombre de lignes écrites.
    """
    try:
        with open(input_path, encoding="utf-8") as jf:
            data = json.load(jf)
    except FileNotFoundError:
        print(f"Erreur : {input_path} introuvable", file=sys.stderr)
        return 0
    except json.JSONDecodeError as exc:
        print(f"Erreur : JSON invalide dans {input_path} : {exc.msg} à la ligne {exc.lineno}", file=sys.stderr)
        return 0

    if not isinstance(data, list) or not data:
        print(f"Erreur : tableau JSON non vide attendu dans {input_path}", file=sys.stderr)
        return 0

    # Collecte toutes les clés uniques sur tous les enregistrements — gère les schémas incohérents
    all_keys: list[str] = []
    seen: set[str] = set()
    for record in data:
        for key in record:
            if key not in seen:
                all_keys.append(key)
                seen.add(key)

    with open(output_path, "w", newline="", encoding="utf-8") as cf:
        writer = csv.DictWriter(cf, fieldnames=all_keys, restval="", extrasaction="ignore")
        writer.writeheader()
        writer.writerows(data)

    return len(data)

rows = json_file_to_csv("deploy_logs.json", "deploy_logs.csv")
print(f"Écrit {rows} lignes dans deploy_logs.csv")

Réponse d'API HTTP — Récupérer et Convertir

Python 3.8+ — récupérer du JSON depuis une API et sauvegarder en CSV
import json
import csv
import urllib.request
import urllib.error

def api_response_to_csv(url: str, output_path: str) -> int:
    """Récupère du JSON depuis un endpoint API REST et l'écrit en CSV."""
    try:
        req = urllib.request.Request(url, headers={"Accept": "application/json"})
        with urllib.request.urlopen(req, timeout=30) as resp:
            if resp.status != 200:
                print(f"Erreur : l'API a retourné le statut {resp.status}")
                return 0
            body = resp.read().decode("utf-8")
    except urllib.error.URLError as exc:
        print(f"Erreur : impossible d'atteindre {url} : {exc.reason}")
        return 0

    try:
        records = json.loads(body)
    except json.JSONDecodeError as exc:
        print(f"Erreur : l'API a retourné un JSON invalide : {exc.msg}")
        return 0

    if not isinstance(records, list) or not records:
        print("Erreur : tableau JSON non vide attendu de l'API")
        return 0

    with open(output_path, "w", newline="", encoding="utf-8") as cf:
        writer = csv.DictWriter(cf, fieldnames=records[0].keys())
        writer.writeheader()
        writer.writerows(records)

    return len(records)

rows = api_response_to_csv(
    "https://api.internal.example.com/v2/deployments?status=completed",
    "completed_deployments.csv",
)
print(f"Exporté {rows} déploiements en CSV")
Note :L'exemple ci-dessus utilise urllib de la bibliothèque standard pour garder le script sans dépendance. Si vous avez requests installé, remplacez la section urllib par resp = requests.get(url, timeout=30); records = resp.json()— le reste du code d'écriture CSV reste identique.

Conversion JSON vers CSV en ligne de commande

Parfois vous avez juste besoin d'une ligne de commande dans le terminal. L'option -c de Python vous permet d'exécuter une conversion rapide sans créer de fichier script. Pour des transformations plus complexes, redirigez d'abord vers jq pour reformater les données, puis convertissez.

bash — conversion json vers csv en une ligne
# Python one-liner : lit le JSON depuis stdin, écrit le CSV sur stdout
cat orders.json | python3 -c "
import json, csv, sys
data = json.load(sys.stdin)
w = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
w.writeheader()
w.writerows(data)
"

# Sauvegarder la sortie dans un fichier
cat orders.json | python3 -c "
import json, csv, sys
data = json.load(sys.stdin)
w = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
w.writeheader()
w.writerows(data)
" > orders.csv
bash — script CLI autonome avec argparse
# Sauvegarder sous json2csv.py et exécuter : python3 json2csv.py input.json -o output.csv
python3 -c "
import json, csv, argparse, sys

parser = argparse.ArgumentParser(description='Convertit un tableau JSON en CSV')
parser.add_argument('input', help='Chemin vers le fichier JSON')
parser.add_argument('-o', '--output', default=None, help='Chemin CSV de sortie (défaut : stdout)')
parser.add_argument('-d', '--delimiter', default=',', help='Délimiteur CSV')
args = parser.parse_args()

with open(args.input) as f:
    data = json.load(f)

out = open(args.output, 'w', newline='') if args.output else sys.stdout
writer = csv.DictWriter(out, fieldnames=data[0].keys(), delimiter=args.delimiter)
writer.writeheader()
writer.writerows(data)
if args.output:
    out.close()
    print(f'Écrit {len(data)} lignes dans {args.output}', file=sys.stderr)
" "$@"
bash — utilisation de jq + csvkit pour des transformations complexes
# Installer csvkit : pip install csvkit

# jq aplatit et sélectionne les champs, in2csv gère le formatage CSV
cat api_response.json | jq '[.[] | {id: .order_id, customer: .customer.name, total}]' | in2csv -f json > orders.csv

# Miller (mlr) est une autre option pour JSON-vers-CSV
mlr --json2csv cat orders.json > orders.csv

Miller (mlr) est un binaire autonome qui traite JSON, CSV et TSV comme des formats de première classe sans runtime Python requis. L'option --json2csv convertit l'entrée JSON en CSV en une seule passe, et vous pouvez enchaîner les verbes Miller pour filtrer, trier ou renommer les colonnes dans la même commande avant d'écrire la sortie. Installez via Homebrew sur macOS (brew install miller) ou votre gestionnaire de paquets Linux. C'est particulièrement utile dans les pipelines CI où vous souhaitez une conversion JSON-vers-CSV rapide sans démarrer un environnement Python.

Alternative haute performance — pandas avec pyarrow

Pour les jeux de données de l'ordre de dizaines de millions de lignes, pandas avec le backend pyarrow lit et écrit significativement plus vite que la version par défaut. Le moteur Arrow en C traite les données en colonnes plus efficacement que le module csv Python ligne par ligne. L'API reste la même — il suffit de définir le paramètre engine.

bash — installer pyarrow
pip install pyarrow
Python 3.8+ — pandas avec pyarrow pour une écriture CSV plus rapide
import pandas as pd

# Lire le JSON avec le moteur pyarrow (analyse plus rapide pour les grands fichiers)
df = pd.read_json("sensor_readings.json", engine="pyarrow")

# to_csv n'a pas de paramètre engine, mais les opérations DataFrame
# entre lecture et écriture bénéficient de la disposition en colonnes de pyarrow
df.to_csv("sensor_readings.csv", index=False)

# Pour les exports vraiment volumineux, envisagez d'écrire en Parquet plutôt qu'en CSV
# — format binaire, 5-10x plus petit, préserve les types
df.to_parquet("sensor_readings.parquet", engine="pyarrow")

Si vous traitez plus de quelques centaines de Mo de JSON et que le consommateur final accepte Parquet, ignorez complètement le CSV. Parquet est plus petit, préserve les types de colonnes, et Redshift comme BigQuery le chargent nativement. CSV est un format avec perte — chaque valeur devient une chaîne.

Affichage dans le terminal avec coloration syntaxique

La bibliothèque rich affiche des tableaux avec des bordures, un alignement et de la couleur dans le terminal — utile pour prévisualiser une conversion pendant le développement sans ouvrir le fichier de sortie.

bash — installer rich
pip install rich
Python 3.8+ — prévisualiser la sortie CSV dans le terminal avec rich
import json
from rich.console import Console
from rich.table import Table

json_string = """
[
  {"hostname": "web-prod-1", "cpu_percent": 72.3, "memory_mb": 3840, "uptime_hours": 720},
  {"hostname": "web-prod-2", "cpu_percent": 45.1, "memory_mb": 2560, "uptime_hours": 168},
  {"hostname": "db-replica-1", "cpu_percent": 91.7, "memory_mb": 7680, "uptime_hours": 2160}
]
"""

records = json.loads(json_string)
console = Console()

table = Table(title="Aperçu des métriques serveur", show_lines=True)
for key in records[0]:
    table.add_column(key, style="cyan" if key == "hostname" else "white")

for row in records:
    table.add_row(*[str(v) for v in row.values()])

console.print(table)
# Affiche un tableau coloré avec des bordures dans le terminal
Avertissement :Rich est uniquement pour l'affichage dans le terminal. Ne l'utilisez pas pour générer des fichiers CSV — il ajoute des codes d'échappement ANSI qui corrompront la sortie. Écrivez dans des fichiers avec csv.DictWriter ou DataFrame.to_csv(), et utilisez rich uniquement pour la prévisualisation.

Travailler avec de grands fichiers JSON

json.load() lit l'intégralité du fichier en mémoire. Pour un fichier JSON de 200 Mo, cela représente ~200 Mo de texte brut plus la surcharge des objets Python — facilement 500 Mo+ d'utilisation du tas. Pour les fichiers de plus de 100 Mo, streamez l'entrée avec ijson et écrivez les lignes CSV au fur et à mesure.

bash — installer ijson
pip install ijson

Streaming d'un tableau JSON vers CSV avec ijson

Python 3.8+ — streamer un grand tableau JSON vers CSV avec une mémoire constante
import ijson
import csv

def stream_json_to_csv(json_path: str, csv_path: str) -> int:
    """Convertit un grand tableau JSON en CSV sans tout charger en mémoire."""
    with open(json_path, "rb") as jf, open(csv_path, "w", newline="", encoding="utf-8") as cf:
        # ijson.items yield chaque élément du tableau de premier niveau un à la fois
        records = ijson.items(jf, "item")

        first_record = next(records)
        fieldnames = list(first_record.keys())

        writer = csv.DictWriter(cf, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerow(first_record)

        count = 1
        for record in records:
            writer.writerow(record)
            count += 1

    return count

rows = stream_json_to_csv("clickstream_2026_03.json", "clickstream_2026_03.csv")
print(f"Streamé {rows} enregistrements en CSV")

NDJSON / JSON Lines — Un objet par ligne

NDJSON (JSON délimité par saut de ligne), aussi appelé JSON Lines ou .jsonl, stocke un objet JSON valide par ligne sans tableau englobant. Ce format est courant dans les pipelines de logs, les flux d'événements (Kafka, Kinesis), et les exports en masse de services comme Elasticsearch et BigQuery. Comme chaque ligne est un objet JSON autonome, vous pouvez traiter un fichier NDJSON avec une simple boucle Python for sur le handle de fichier — pas besoin de la bibliothèque ijson. La mémoire reste constante quelle que soit la taille du fichier, ce qui en fait l'approche de streaming la plus simple lorsque vos données source sont déjà au format JSON Lines.

Python 3.8+ — convertir NDJSON en CSV ligne par ligne
import json
import csv

def ndjson_to_csv(ndjson_path: str, csv_path: str) -> int:
    """Convertit un fichier JSON délimité par saut de ligne en CSV, une ligne à la fois."""
    with open(ndjson_path, encoding="utf-8") as nf:
        first_line = nf.readline()
        first_record = json.loads(first_line)
        fieldnames = list(first_record.keys())

        with open(csv_path, "w", newline="", encoding="utf-8") as cf:
            writer = csv.DictWriter(cf, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerow(first_record)

            count = 1
            for line in nf:
                line = line.strip()
                if not line:
                    continue
                try:
                    record = json.loads(line)
                    writer.writerow(record)
                    count += 1
                except json.JSONDecodeError:
                    continue  # ignorer les lignes malformées

    return count

rows = ndjson_to_csv("access_log.ndjson", "access_log.csv")
print(f"Converti {rows} entrées de log en CSV")
Note :Passez au streaming lorsque le fichier JSON dépasse 100 Mo. Un tableau JSON de 1 Go chargé avec json.load() peut consommer 3–5 Go de RAM en raison de la surcharge des objets Python. Avec ijson, la mémoire reste stable quelle que soit la taille du fichier. Si vous avez juste besoin de convertir rapidement un petit fichier, collez-le dans le convertisseur JSON vers CSV à la place.

Erreurs courantes

newline='' manquant dans open() — lignes vides sous Windows

Problème : Le module csv écrit des fins de ligne . Sans newline='', le mode texte de Python ajoute un supplémentaire sous Windows, produisant une sortie à double espacement.

Solution : Passez toujours newline='' lors de l'ouverture d'un fichier pour l'écriture CSV. C'est inoffensif sur macOS/Linux.

Before · Python
After · Python
with open("output.csv", "w") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Lignes vides entre chaque ligne de données sous Windows
with open("output.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Sortie propre sur toutes les plateformes
Oublier index=False dans pandas to_csv()

Problème : Sans index=False, pandas ajoute une colonne de numéros de ligne auto-incrémentés (0, 1, 2, ...) qui pollue le CSV avec des données qui n'étaient pas dans le JSON original.

Solution : Passez index=False à to_csv(). Si vous avez réellement besoin d'une colonne d'index, nommez-la explicitement avec df.index.name = 'row_num'.

Before · Python
After · Python
df = pd.read_json("events.json")
df.to_csv("events.csv")
# Le CSV reçoit une colonne sans nom supplémentaire : ,event_id,timestamp,...
# La virgule de tête casse de nombreux parseurs CSV
df = pd.read_json("events.json")
df.to_csv("events.csv", index=False)
# CSV propre : event_id,timestamp,...
Utiliser records[0].keys() quand les enregistrements ont des clés incohérentes

Problème : Si les objets JSON ont des clés différentes (certains enregistrements ont des champs optionnels), utiliser les clés du premier enregistrement comme fieldnames supprime silencieusement les colonnes qui n'apparaissent que dans les enregistrements ultérieurs.

Solution : Collectez toutes les clés uniques sur tous les enregistrements avant de créer le DictWriter.

Before · Python
After · Python
records = json.load(f)
writer = csv.DictWriter(out, fieldnames=records[0].keys())
# Manque le champ "discount" qui n'apparaît que dans records[2]
records = json.load(f)
all_keys = list(dict.fromkeys(k for r in records for k in r))
writer = csv.DictWriter(out, fieldnames=all_keys, restval="")
# Chaque clé de chaque enregistrement est incluse comme colonne
Écrire des dicts imbriqués directement en CSV sans aplatissement

Problème : csv.DictWriter appelle str() sur les dicts imbriqués, produisant des colonnes avec des valeurs comme "{'city': 'Portland'}"— repr Python brute, pas des données réelles.

Solution : Aplatissez d'abord les objets imbriqués en utilisant pd.json_normalize() ou une fonction d'aplatissement personnalisée.

Before · Python
After · Python
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
writer = csv.DictWriter(f, fieldnames=["id", "meta"])
writer.writerows(records)
# la colonne meta contient : {'source': 'web', 'region': 'us-west'}
import pandas as pd
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
df = pd.json_normalize(records, sep="_")
df.to_csv("events.csv", index=False)
# Colonnes : id, meta_source, meta_region

csv.DictWriter vs pandas — Comparaison rapide

Méthode
JSON imbriqué
Types personnalisés
Streaming
Dépendances
Installation requise
csv.DictWriter
✗ (aplatissement manuel)
✓ (ligne par ligne)
Aucune
Non (stdlib)
csv.writer
✓ (ligne par ligne)
Aucune
Non (stdlib)
pd.DataFrame.to_csv()
✗ (plat uniquement)
✓ (via dtypes)
pandas + numpy
pip install
pd.json_normalize() + to_csv()
✓ (via dtypes)
pandas + numpy
pip install
csv.writer + json_flatten
flatten_json
pip install
jq + csvkit (CLI)
✓ (via jq)
N/A
jq, csvkit
Installation système

Utilisez csv.DictWriter quand vous avez besoin de zéro dépendances, que votre JSON est plat, et que le script s'exécute dans un environnement restreint (conteneurs CI, fonctions Lambda, Python embarqué). Utilisez pd.json_normalize() + to_csv() quand le JSON est imbriqué, que vous devez transformer ou filtrer les données avant l'export, ou que vous êtes déjà dans un workflow pandas. Pour les fichiers ne tenant pas en mémoire, combinez ijson avec csv.DictWriter pour un streaming à mémoire constante.

Pour des conversions rapides sans code, le convertisseur JSON vers CSV sur ToolDeck s'en charge sans aucune configuration Python.

Foire aux questions

Comment convertir JSON en CSV en Python sans pandas ?

Utilisez les modules intégrés json et csv. Appelez json.load() pour analyser le fichier JSON en une liste de dicts, extrayez les fieldnames depuis les clés du premier dict, créez un csv.DictWriter, appelez writeheader(), puis writerows(). Cette approche n'a aucune dépendance externe et fonctionne dans tout environnement Python 3.x. Elle est aussi plus rapide que pandas pour les petits fichiers car il n'y a pas d'allocation de DataFrame. Si vos objets JSON ont des clés incohérentes entre les enregistrements, collectez d'abord toutes les clés uniques avec dict.fromkeys(k for r in records for k in r) avant de les passer comme fieldnames pour éviter les colonnes manquantes.

Python
import json
import csv

with open("orders.json") as f:
    records = json.load(f)

with open("orders.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=records[0].keys())
    writer.writeheader()
    writer.writerows(records)

Comment gérer le JSON imbriqué lors de la conversion en CSV ?

Les tableaux JSON plats s'associent directement aux lignes CSV, mais les objets imbriqués doivent d'abord être aplatis. Avec pandas, pd.json_normalize() gère cela automatiquement — il joint les clés imbriquées avec un séparateur point (ex. : "address.city"). Sans pandas, écrivez une fonction récursive qui parcourt le dict et concatène les clés avec un délimiteur. Pour les structures profondément imbriquées avec plusieurs niveaux, json_normalize les traite tous en une seule passe. Le paramètre sep contrôle le caractère de jonction entre les segments de clé — le tiret bas est généralement plus sûr que le point par défaut pour les imports SQL et la compatibilité des formules de tableur.

Python
import pandas as pd

nested_data = [
    {"id": "ord_91a3", "customer": {"name": "Marie Dupont", "email": "m.dupont@exemple.fr"}},
]
df = pd.json_normalize(nested_data, sep="_")
# Colonnes : id, customer_name, customer_email
df.to_csv("flat_orders.csv", index=False)

Pourquoi mon CSV a-t-il des lignes vides entre les lignes de données sous Windows ?

Le module csv écrit des fins de ligne \r\n par défaut. Sous Windows, ouvrir le fichier en mode texte ajoute un \r supplémentaire, produisant \r\r\n — ce qui s'affiche comme une ligne vide. La solution est de toujours passer newline="" à open(). Cela indique à Python de ne pas traduire les fins de ligne, laissant le module csv les gérer. Ce modèle est requis quel que soit le système d'exploitation — il est inoffensif sur macOS et Linux, et critique sous Windows. La documentation Python le signale explicitement dans la section du module csv comme la manière correcte d'ouvrir des fichiers pour l'écriture CSV.

Python
# Incorrect — lignes vides sous Windows
with open("output.csv", "w") as f:
    writer = csv.writer(f)

# Correct — newline="" empêche le double \r
with open("output.csv", "w", newline="") as f:
    writer = csv.writer(f)

Comment ajouter des enregistrements JSON à un fichier CSV existant ?

Ouvrez le fichier en mode ajout ("a") et créez un DictWriter avec les mêmes fieldnames. Ignorez writeheader() puisque la ligne d'en-tête existe déjà. Avec pandas, utilisez to_csv(mode="a", header=False). Assurez-vous que l'ordre des colonnes correspond au fichier existant, sinon les données atterriront dans les mauvaises colonnes. Si vous n'êtes pas sûr de l'ordre des colonnes dans le fichier existant, ouvrez-le d'abord avec csv.DictReader et lisez les fieldnames depuis son attribut fieldnames avant de créer le writer pour l'ajout.

Python
import csv

new_records = [
    {"order_id": "ord_f4c1", "total": 89.50, "status": "shipped"},
]

with open("orders.csv", "a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["order_id", "total", "status"])
    writer.writerows(new_records)

Quelle est la façon la plus rapide de convertir un grand fichier JSON en CSV en Python ?

Pour les fichiers de moins de 500 Mo, pd.read_json() suivi de to_csv() est l'approche la plus rapide en un seul appel — pandas utilise du code C optimisé en interne. Pour les fichiers au-dessus de 500 Mo, utilisez ijson pour streamer les enregistrements JSON et les écrire en CSV avec csv.DictWriter ligne par ligne. Cela maintient une utilisation mémoire constante quelle que soit la taille du fichier. Pour les fichiers NDJSON (un objet JSON par ligne), vous n'avez pas besoin d'ijson du tout — une simple boucle Python for sur le handle de fichier traite chaque ligne indépendamment et atteint une mémoire constante sans aucune bibliothèque tierce.

Python
# Rapide pour les fichiers tenant en mémoire
import pandas as pd
df = pd.read_json("large_dataset.json")
df.to_csv("large_dataset.csv", index=False)

# Streaming pour les fichiers ne tenant pas en mémoire
import ijson, csv
with open("huge.json", "rb") as jf, open("huge.csv", "w", newline="") as cf:
    records = ijson.items(jf, "item")
    first = next(records)
    writer = csv.DictWriter(cf, fieldnames=first.keys())
    writer.writeheader()
    writer.writerow(first)
    for record in records:
        writer.writerow(record)

Puis-je écrire la sortie CSV sur stdout au lieu d'un fichier en Python ?

Oui. Passez sys.stdout comme objet fichier à csv.writer() ou csv.DictWriter(). C'est utile pour rediriger la sortie dans des scripts shell ou pour un débogage rapide. Avec pandas, appelez to_csv(sys.stdout, index=False) ou to_csv(None) pour obtenir une chaîne que vous pouvez afficher. Aucun fichier temporaire nécessaire. Lors de l'écriture sur stdout sous Windows, appelez d'abord sys.stdout.reconfigure(newline="") pour éviter le problème du double retour chariot, car stdout s'ouvre en mode texte par défaut.

Python
import csv
import sys
import json

data = json.loads('[{"host":"web-1","cpu":72.3},{"host":"web-2","cpu":45.1}]')
writer = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
# host,cpu
# web-1,72.3
# web-2,45.1

Outils connexes

MS
Maria SantosBackend Developer

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.

PS
Priya SharmaRéviseur technique

Priya is a data scientist and machine learning engineer who has worked across the full Python data stack — from raw data ingestion and cleaning to model deployment and monitoring. She is passionate about reproducible research, Jupyter-based workflows, and the practical engineering side of ML. She writes about NumPy, Pandas, data serialisation, and the Python patterns that make data pipelines reliable at scale.