1 05/05/2026 10 min

Perché la conversione Parquet → JSON non è banale

Parquet e JSON risolvono problemi diversi. Parquet è pensato per archiviazione colonnare, compressione efficiente e letture analitiche; JSON è un formato testuale, flessibile e comodo da passare ad API, frontend o tool di integrazione. Quando converti un file Parquet in JSON con Python, non stai solo cambiando estensione: stai decidendo come gestire tipi numerici, valori nulli, timestamp, array annidati e volumi di dati che possono crescere rapidamente.

La scelta più importante è questa: vuoi un JSON compatto per scambio dati, oppure un JSON line-delimited per elaborazione streaming? La differenza cambia lo script, il consumo di RAM e persino il modo in cui consumi il risultato da altri sistemi.

Se il file è piccolo, la conversione è semplice. Se il file è grande, leggere tutto in memoria e serializzare in un colpo solo può diventare un problema. Per questo conviene partire con una soluzione corretta, ma anche scalabile.

Scelta del motore Python: pandas o pyarrow

In pratica hai due strade comuni. pandas è più immediato e leggibile, ottimo per conversioni veloci e per chi lavora già con DataFrame. pyarrow è più vicino al formato Parquet, spesso più efficiente e più adatto quando vuoi controllare meglio schema e serializzazione.

Per un uso quotidiano, pandas basta nella maggior parte dei casi. Per file grossi o pipeline dati più rigorose, pyarrow ti dà un controllo migliore e spesso meno sorprese sui tipi. La scelta dipende soprattutto da tre fattori: dimensione del file, struttura annidata e necessità di output JSON standard o JSON Lines.

Installazione dei pacchetti necessari

Per iniziare con un ambiente pulito, crea un virtualenv e installa i pacchetti minimi. Se usi già un ambiente gestito, adatta il comando al tuo flusso.

python3 -m venv .venv
source .venv/bin/activate
pip install pandas pyarrow

Se vuoi solo una conversione semplice, pandas e pyarrow sono sufficienti. In molti casi pandas usa pyarrow sotto il cofano per leggere Parquet, quindi tenerli entrambi installati evita errori di dipendenza o differenze di comportamento tra ambienti.

Conversione base con pandas

Il flusso più diretto è: leggi il Parquet in un DataFrame, poi serializza in JSON. Questo approccio è pulito e facile da mantenere.

import pandas as pd

input_file = "input.parquet"
output_file = "output.json"

df = pd.read_parquet(input_file)
df.to_json(output_file, orient="records", indent=2, force_ascii=False)

Con orient="records" ottieni una lista di oggetti JSON, uno per riga logica del DataFrame. È il formato più comodo quando devi passare i dati a un’applicazione web o a un’API. L’opzione indent=2 rende il file leggibile, mentre force_ascii=False preserva correttamente caratteri accentati e testo UTF-8.

Se vuoi verificare subito il risultato, controlla che il file esista e che il JSON sia valido.

python -m json.tool output.json | head

Se il comando restituisce un output formattato, la struttura è valida. Se fallisce, il problema è quasi sempre nella serializzazione, nei valori non supportati o in un file parziale scritto da uno script interrotto.

Come gestire tipi dati che JSON non ama

JSON non ha un tipo nativo per timestamp complessi, Decimal, NaN o strutture binarie. Parquet invece può contenerli senza problemi. Qui nasce la parte delicata della conversione.

I casi più frequenti sono questi: date e timestamp, valori nulli, numeri decimali e colonne con oggetti annidati. Se non controlli il mapping, il risultato può essere corretto ma poco interoperabile, oppure formalmente JSON ma difficile da consumare da altri sistemi.

Con pandas, molti valori vengono convertiti in modo automatico. Però è bene sapere cosa aspettarsi: i timestamp possono finire in millisecondi Unix o in stringhe ISO a seconda delle opzioni, i null diventano null, mentre i NaN possono richiedere attenzione perché alcuni parser JSON li gestiscono male o li rifiutano.

Se vuoi un comportamento più prevedibile, conviene normalizzare prima della serializzazione.

import pandas as pd

input_file = "input.parquet"
df = pd.read_parquet(input_file)

# Esempio: conversione esplicita dei timestamp in stringa ISO
for col in df.select_dtypes(include=["datetime64[ns]", "datetime64[ns, UTC]"]):
    df[col] = df[col].dt.strftime("%Y-%m-%dT%H:%M:%S%z")

# Sostituzione dei NaN con None per un JSON più pulito
df = df.where(pd.notnull(df), None)

print(df.to_json(orient="records", indent=2, force_ascii=False))

Questo esempio mostra il principio, non una ricetta universale. La gestione dei timestamp dipende dal formato atteso dal sistema che consumerà il JSON. Se il downstream vuole epoch milliseconds, la conversione va adattata. Se vuole stringhe ISO 8601, il formato va fissato e documentato.

Quando il file è grande: meglio JSON Lines

Se il Parquet contiene centinaia di migliaia o milioni di righe, un unico array JSON può diventare pesante da generare e ingestire. In questi casi è spesso più pratico produrre JSON Lines o NDJSON: una riga JSON per record.

Questo formato è molto usato per pipeline, log processing e caricamenti incrementali. Ogni riga è autonoma, quindi puoi streammare, comprimere e consumare i dati senza aspettare l’intero file in memoria.

import pandas as pd

input_file = "input.parquet"
output_file = "output.ndjson"

df = pd.read_parquet(input_file)

df.to_json(output_file, orient="records", lines=True, force_ascii=False)

Qui lines=True cambia completamente il formato di output. Il file non è più un array JSON unico, ma una sequenza di oggetti separati da newline. È più facile da processare in streaming, ma meno comodo se ti serve un singolo documento JSON da servire via web.

Un dettaglio operativo utile: molti tool da shell capiscono bene NDJSON. Puoi ispezionarlo con head, filtrarlo con jq e caricarlo in sistemi che supportano record per riga.

head -n 3 output.ndjson
jq -c '. | {id, name}' output.ndjson | head

Conversione con pyarrow per più controllo

Quando vuoi restare più vicino al formato Parquet, pyarrow è una scelta solida. È utile soprattutto se hai schemi complessi o vuoi costruire un flusso più esplicito.

import pyarrow.parquet as pq
import json

input_file = "input.parquet"
output_file = "output.json"

table = pq.read_table(input_file)
records = table.to_pylist()

with open(output_file, "w", encoding="utf-8") as f:
    json.dump(records, f, ensure_ascii=False, indent=2)

Qui la tabella Arrow viene convertita in lista di dizionari Python con to_pylist(). È una strada chiara e leggibile, ma attenzione: anche in questo caso il dataset completo finisce in memoria. Se il file è grande, l’approccio va ripensato con chunk o con elaborazione a blocchi.

Il vantaggio di pyarrow è che spesso conserva meglio il legame tra schema e dati. Se devi ispezionare colonne annidate, array o campi molto specifici, partire da Arrow rende più facile ragionare sul contenuto reale del file Parquet.

Scrivere uno script riusabile da riga di comando

In un contesto operativo, conviene incapsulare la conversione in uno script parametrico. In questo modo eviti di modificare il codice a mano ogni volta e puoi integrarlo in job schedulati, pipeline CI o automazioni di data exchange.

import argparse
import pandas as pd

parser = argparse.ArgumentParser(description="Converti Parquet in JSON")
parser.add_argument("input", help="Path del file Parquet")
parser.add_argument("output", help="Path del file JSON")
parser.add_argument("--lines", action="store_true", help="Esporta in JSON Lines")
args = parser.parse_args()

df = pd.read_parquet(args.input)

if args.lines:
    df.to_json(args.output, orient="records", lines=True, force_ascii=False)
else:
    df.to_json(args.output, orient="records", indent=2, force_ascii=False)

Con questo script puoi scegliere il formato al volo. È una base semplice, ma già utile in produzione. Se vuoi migliorarlo, aggiungi controlli su esistenza file, gestione eccezioni e validazione dello schema prima dell’export.

Esecuzione tipica:

python convert_parquet_json.py input.parquet output.json
python convert_parquet_json.py input.parquet output.ndjson --lines

Validazione pratica del risultato

Una conversione riuscita non basta: il file deve essere anche consumabile. Il controllo minimo è verificare che il JSON sia sintatticamente valido, che il numero di record sia coerente e che i campi critici non abbiano subito trasformazioni indesiderate.

Per una verifica rapida, confronta righe e colonne tra sorgente e destinazione. Con pandas puoi farlo in pochi secondi.

import pandas as pd

src = pd.read_parquet("input.parquet")
dst = pd.read_json("output.json")

print(src.shape)
print(dst.shape)
print(src.columns.tolist())
print(dst.columns.tolist())

Se stai producendo NDJSON, usa read_json(..., lines=True). Se i campi differiscono, il problema può essere il tipo di esportazione, una colonna annidata non appiattita oppure una serializzazione che ha convertito oggetti complessi in stringhe.

In ambienti più rigorosi, aggiungi un test automatico che confronti un sottoinsieme di righe campione. È il modo più semplice per evitare regressioni quando cambia lo schema del Parquet o quando aggiorni la libreria Python.

Errori tipici e come leggerli

Quando la conversione fallisce, gli errori più comuni sono abbastanza riconoscibili. File non trovato: path errato o mount mancante. Schema incompatibile: colonne con tipi non coerenti o file Parquet generato da un sistema diverso. Memory error: dataset troppo grande per l’approccio in memoria. JSON non valido: dati non serializzabili o output interrotto.

Un buon riflesso operativo è leggere il messaggio d’errore fino in fondo e non fermarsi al primo stack trace. In molti casi il punto davvero utile è la riga finale, che indica il campo o il tipo problematico.

Se il problema è la memoria, il fix non è “aumentare la RAM” e basta. Prima prova a passare a JSON Lines, poi valuta chunking o una pipeline che trasformi e scriva blocchi più piccoli. Se il problema è un tipo non serializzabile, normalizza il campo prima del dump.

Una variante più robusta per dataset annidati

Se il Parquet contiene strutture annidate, la conversione diretta può produrre JSON tecnicamente valido ma poco leggibile o difficile da usare. In quel caso conviene ispezionare il contenuto prima di esportarlo e, se serve, appiattire i campi rilevanti.

import pandas as pd

# Flatten base di colonne annidate dopo lettura
# Nota: il comportamento dipende dalla struttura reale del Parquet

df = pd.read_parquet("input.parquet")

# Esempio: espansione di una colonna contenente dizionari
if "payload" in df.columns:
    payload_df = pd.json_normalize(df["payload"])
    df = df.drop(columns=["payload"]).join(payload_df)

print(df.to_json(orient="records", lines=True, force_ascii=False))

Questo approccio è utile quando vuoi produrre JSON più lineare per integrazione con altri sistemi. Però va usato con criterio: appiattire tutto indiscriminatamente può generare nomi di colonna lunghi, perdita di contesto e output difficile da mantenere.

Quando conviene evitare la conversione diretta

Non sempre Parquet → JSON è la scelta giusta. Se il consumo finale è analitico, meglio restare su Parquet o convertire in un formato colonnare equivalente. Se il dataset è molto grande e deve essere interrogato spesso, JSON aumenta peso, tempi di parsing e costo di trasferimento.

La conversione ha senso quando devi integrare con un sistema che parla solo JSON, quando vuoi esporre un dataset via API, oppure quando devi generare un payload temporaneo per automazioni e scambi tra servizi. Fuori da questi casi, JSON è spesso un compromesso comodo ma non il più efficiente.

Riepilogo operativo

Per convertire Parquet in JSON con Python, il percorso più semplice passa da pandas: leggi il file, normalizza i campi problematici, scegli tra JSON classico e JSON Lines, poi valida l’output. Se vuoi più controllo sullo schema, pyarrow è una buona alternativa. Se il dataset cresce, la priorità diventa la memoria, non la sintassi.

La regola pratica è questa: usa JSON classico per documenti piccoli o per output destinati a interfacce web, usa NDJSON per flussi e pipeline, normalizza i tipi non standard prima del dump e verifica sempre il risultato con un controllo automatico. È il modo più pulito per evitare conversioni “riuscite” solo sulla carta.

Assunzione operativa: i file Parquet contengono dati tabellari leggibili da pandas o pyarrow e il target finale accetta JSON UTF-8 senza requisiti proprietari aggiuntivi.