Vai al contenuto
jsonbeautifiers
Italiano

NDJSON in JSON

Trasforma record separati da a capo in un unico array, e segnala la riga che fallisce.

NDJSON
Array JSON

Nulla di ciò che incolli lascia il tuo browser. L’allowlist connect-src ne fa una garanzia del browser anziché una promessa. Verificalo tu stesso

Trasforma JSON delimitato da a capo in un unico array. Ogni riga viene analizzata in modo indipendente, e qualsiasi riga che fallisce viene segnalata con il suo numero invece di far cadere l’intero file.

NDJSON è ciò che esce da pipeline di log, esportazioni BigQuery, file bulk di Elasticsearch e API in streaming, ed è ciò che provoca gli errori «Extra data» e «unexpected non-whitespace character» quando lo si analizza come un unico documento.

Che cos’è NDJSON

Un valore JSON completo per riga, separati da un a capo. Nessuna virgola tra i record, nessun array che li racchiude. Le righe vuote vengono ignorate. Le estensioni convenzionali sono .ndjson e .jsonl.

JSON Lines e NDJSON sono di fatto lo stesso formato descritto da due piccole specifiche che concordano su tutto ciò che conta. Alcuni strumenti nominano l’uno e altri l’altro; un file scritto per uno viene letto correttamente da entrambi.

Perché esiste

Tre vantaggi concreti, tutti derivanti dal fatto che i record sono indipendenti.

Si può trasmettere in streaming
Chi lo consuma elabora un record alla volta e non tiene mai in mano l’intero file. Un export da 50 GB va bene; un array JSON da 50 GB no.
Si può accodare
Aggiungere un record è una sola scrittura alla fine del file. Accodare a un array JSON significa riscrivere la parentesi di chiusura, e non è affatto un’accodatura.
Sopravvive alla corruzione
Una riga malformata ti costa un record. Un byte malformato in un array JSON ti costa il file.

In quale direzione convertire

Verso un array quando i dati vanno dove ci si aspetta un documento unico: un browser, il corpo di una richiesta, un file di configurazione. Verso NDJSON quando finiscono in una pipeline, in un log, in un file append-only o in qualunque cosa faccia streaming. Entrambe le direzioni sono qui sopra.

How to do this in code

Leggere e scrivere NDJSON nel codice.

py Python

La list comprehension tiene tutto in memoria. Itera direttamente sul file per elaborarlo in streaming.

import json

# Read
with open('events.ndjson') as f:
    records = [json.loads(line) for line in f if line.strip()]

# Write
with open('events.ndjson', 'w') as f:
    for r in records:
        f.write(json.dumps(r) + '\n')

# pandas knows the format
import pandas as pd
df = pd.read_json('events.ndjson', lines=True)
sh jq

-s raccoglie tutti gli input in un array; -c scrive un valore compatto per riga. Quelle due opzioni sono l’intera conversione.

# NDJSON to an array
jq -s . events.ndjson > events.json

# An array to NDJSON
jq -c '.[]' events.json > events.ndjson

# Filter a huge NDJSON file without loading it all
jq -c 'select(.level == "error")' events.ndjson
js Node

crlfDelay: Infinity fa trattare a readline il CRLF come un solo a capo, cosa che conta per i file scritti su Windows.

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

const rl = createInterface({
  input: createReadStream('events.ndjson'),
  crlfDelay: Infinity,
});

for await (const line of rl) {
  if (!line.trim()) continue;
  const record = JSON.parse(line);
  // one record at a time, constant memory
}

Domande frequenti

NDJSON è la stessa cosa di JSON Lines?
A tutti gli effetti pratici, sì. Sono due piccole specifiche che concordano sulle parti importanti: un valore JSON per riga, UTF-8, separati da a capo. Le estensioni .jsonl e .ndjson si usano in modo intercambiabile.
Un record può occupare più righe?
No. È tutto il senso del formato: l’a capo è il separatore dei record, quindi ogni record deve stare su esattamente una riga. Minifica ogni record prima di scriverlo.
Perché il mio file NDJSON non si analizza come JSON?
Perché non è un documento JSON, sono molti. JavaScript riporta «Unexpected non-whitespace character after JSON» e Python riporta «Extra data». Entrambi significano che il parser ha finito un valore e ne ha trovato un altro.