Da CSV a JSON: i cinque casi che rompono qualsiasi convertitore
Pubblicato il 17/07/2026 · 15 min di lettura · Strumenti per sviluppatori
Daniel Okonkwo — Sviluppatore front-end e redattore Tech presso OneKitly
Performance web · Formati di file
Verificato su 4 fonti
Il CSV non ha una norma, solo la RFC 4180: una RFC informativa che descrive ciò che la maggior parte dei programmi faceva già nel 2005, non una regola vincolante. Cinque casi separano un parser che funziona da uno che corrompe in silenzio. Uno: un delimitatore dentro un campo tra virgolette. name,city / Tom,"Paris, France" resta su due colonne, e una virgoletta raddoppiata all'interno esce come una sola. Due: un a capo dentro un campo tra virgolette. Il parser legge fino alla virgoletta di chiusura e non fino a fine riga, quindi un indirizzo su due righe sopravvive come un solo valore. Tre: i tipi. La conversione è disattivata di default; attivala e 1 diventa il numero 1, mentre 0044, 1.0, 1e3, 2026-08-18 e 9007199254740993 restano stringhe, perché una cella si converte solo se il numero si ristampa esattamente come è arrivato. true diventa un booleano ma TRUE no, perché i booleani JSON sono minuscoli, e null diventa il null di JSON — cosa che sorprende il giorno in cui un cognome è Null. Quattro: intestazioni duplicate. name,name,name dà name, name_2 e name_3 invece di una sola colonna superstite, e un'intestazione vuota diventa column2. Cinque: la codifica. Il BOM UTF-8 viene tolto, e il browser assorbe i BOM di UTF-8 e UTF-16 prima che lo strumento veda il testo, ma non c'è un selettore di codifica, quindi un export Windows-1252 arriva come Andr�, e U+FFFD non si disfa. Due casi che non salva: un campo tra virgolette preceduto da uno spazio, Tom, "Paris, France", non è trattato come citato, e una riga di suggerimento Excel sep=; viene consumata come intestazione.
Delimitatori tra virgolette, a capo incorporati, tipi ambigui, intestazioni duplicate e codifica. Ogni caso è passato dal convertitore e qui c'è l'output esatto — compresi i due che non salva.
La RFC 4180 è una descrizione, non una regola
Tutti citano la RFC 4180 come se fosse lo standard del CSV. La sua stessa intestazione dice il contrario: è informativa, il che in gergo IETF significa che non specifica alcuno standard Internet. È stata pubblicata nel 2005 per mettere per iscritto ciò che i programmi già facevano, e lo dice del proprio oggetto: la regola 5 osserva che alcuni programmi, Microsoft Excel compreso, non usano affatto le virgolette. È questa la radice di ogni problema in questo articolo. Non c'è un'autorità a cui appellarsi quando due strumenti divergono sullo stesso file, perché nessuno dei due sta violando alcunché.
La RFC definisce in effetti due cose che aiuterebbero, e nessuna sopravvive al passaggio in un file. Definisce un parametro header sul tipo di media text/csv, con i valori present e absent, per dire al destinatario se la prima riga contiene nomi di colonna. E dice che l'uso comune è US-ASCII, con altri set di caratteri portati dal parametro charset. Sono entrambi parametri MIME: vivono su una risposta HTTP o su un allegato, non dentro i byte. Salva gli stessi dati come vendite.csv ed entrambe le informazioni sono sparite. È per questo che ogni lettore CSV al mondo ha una casella «la prima riga è un'intestazione», ed è per questo che la codifica va indovinata.
Casi 1 e 2 — il delimitatore e l'a capo dentro un campo tra virgolette
Questi due sono lo stesso bug con abiti diversi, e nascono entrambi dallo spezzare sul carattere grezzo invece che analizzare. Un convertitore che fa text.split(",") trasforma Tom,"Paris, France" in tre colonne, e ogni riga sotto eredita la colonna in più. Un convertitore che fa prima text.split("\n") taglia in due un indirizzo su due righe e produce una riga con un solo campo. Il rimedio è lo stesso: percorrere la stringa carattere per carattere, tenere un flag «sono dentro le virgolette», e trattare un delimitatore o un a capo come strutturali solo quando il flag è abbassato.
Passano entrambi. name,address / Tom,"12 rue A\nParis" / Ann,"3 rue B" restituisce esattamente due oggetti, il primo con un indirizzo su due righe. Una virgoletta raddoppiata viene de-escapata in ingresso, quindi "He said ""hi"" loudly" torna come He said "hi" loudly. Vale la pena conoscere uno scarto rispetto alla RFC: lo strumento normalizza ogni CR LF in LF prima di analizzare, perciò un a capo che era CR LF dentro un campo citato esce dal convertitore come LF nudo. Non si perde nulla, ma se confronti l'andata e ritorno byte per byte, la differenza è lì.
Il caso che non salva è quello su cui la RFC è esplicita. La sezione 2.4 dice che gli spazi fanno parte del campo e non vanno ignorati: in Tom, "Paris, France" il primo carattere del campo è uno spazio e la virgoletta che segue è solo un carattere, non un delimitatore di apertura. Il parser dà ragione alla RFC e produce due colonne rotte, " Paris e France". Chi scrive quella riga voleva quasi sempre un campo citato. Se il tuo esportatore mette uno spazio dopo il delimitatore, toglilo prima di convertire, altrimenti le virgolette sono decorative.
Caso 3 — i tipi, e il test di andata e ritorno che salva i numeri di telefono
Il CSV non ha tipi. Ogni cella è testo, e nel momento in cui produci JSON devi decidere se 1 è la stringa "1" o il numero 1. Per impostazione predefinita il convertitore non decide nulla: la conversione è un interruttore e parte spento, quindi un'esecuzione semplice dà un array di oggetti i cui valori sono tutti stringhe. È il default giusto, perché una stringa è sempre recuperabile e un numero no.
Attiva l'interruttore e la regola è un solo test di andata e ritorno: una cella diventa numero solo se ristampare quel numero restituisce esattamente i caratteri arrivati. Eseguendolo, 1 diventa 1, ma 0044 resta "0044" perché Number("0044") stampa 44. 1.0 resta "1.0" perché stampa 1. 1e3 resta "1e3" perché stampa 1000. .5 e +1 restano stringhe per lo stesso motivo. E 9007199254740993 resta una stringa, perché il double più vicino in JavaScript stampa 9007199254740992: un convertitore senza questa protezione cambia in silenzio l'ultima cifra di un identificatore lungo, e nulla a valle te lo dirà mai.
Tre cose sfuggono al test di andata e ritorno, perché passano da un confronto letterale. true e false diventano booleani, ma solo in minuscolo: TRUE, True e FALSE restano stringhe, cosa che conta perché Excel scrive i booleani in maiuscolo e le interfacce francese e tedesca scrivono VRAI e WAHR. null diventa il null di JSON, e lì c'è una trappola vera: un campo di testo il cui valore sono le quattro lettere null non è la stessa cosa di un valore assente, e dopo la conversione non li distingui più. E con le date non si fa nulla: 2026-08-18 resta la stringa "2026-08-18", che è la risposta giusta, perché un convertitore che analizza le date deve scegliere un fuso orario e sbaglierà.
Caso 4 — intestazioni duplicate, intestazioni vuote, righe irregolari
Un CSV può ripetere il nome di una colonna; un oggetto JSON no. Con name,name,name sopra a,b,c, l'implementazione ingenua scrive tre volte la stessa chiave e JSON tiene l'ultima: ottieni {"name": "c"} e due colonne di dati sono sparite senza alcun errore. Questo convertitore rinomina: name, name_2, name_3. Gestisce anche il caso successivo, quando il nome inventato collide con uno vero — name,name,name_2 dà name, name_2 e name_2_2, perché il rinominatore controlla su tutto ciò che è già stato usato e non solo sulle intestazioni originali.
Una cella di intestazione vuota riceve un nome posizionale: name,,name, sopra a,b,c,d dà name, column2, name_2 e column4. I nomi di intestazione vengono ripuliti, quindi " name , age " produce name e age. E la larghezza dell'output è quella della riga più larga del file, non quella dell'intestazione: a,b,c sopra le due righe 1,2 e 3,4,5,6 dà quattro chiavi a ogni oggetto, con c vuoto sulla riga corta e un column4 che porta il 6 di cui l'intestazione non aveva tenuto conto. Non si scarta nulla, ed è la scelta giusta per un convertitore: troncare in silenzio una riga lunga significa distruggere proprio la riga che andava guardata.
Caso 5 — la codifica, quella che lo strumento non può riparare
Un file CSV sono byte. Nulla al suo interno dice quale tabella trasforma quei byte in caratteri, e la RFC 4180 mette quell'informazione in un parametro MIME che un file su disco non porta. Due meccanismi coprono in parte il vuoto. Un byte order mark all'inizio del file identifica UTF-8, UTF-16 LE e UTF-16 BE, e il lettore di file del browser lo consuma: trascina un file UTF-16 LE con BOM nello strumento e il testo arriva decodificato correttamente, con il marchio già tolto. Lo strumento toglie poi di nuovo un BOM per conto suo, il che copre il caso in cui il marchio arriva dagli appunti e non da un file.
Il vuoto che resta aperto è il file senza alcun marchio, che è la maggioranza. Salva un foglio di calcolo come CSV semplice su una macchina Windows dell'Europa occidentale e ottieni Windows-1252, un byte per carattere, senza BOM. Questo convertitore non ha un selettore di codifica: il lettore ripiega su UTF-8, il byte E9 che significava é non è UTF-8 valido, e viene sostituito da U+FFFD. Lo strumento poi analizza senza intoppi e restituisce {"name": "Andr�", "city": "K�ln"} senza alcun avviso, perché dal suo punto di vista non è andato storto nulla. U+FFFD non conserva traccia del byte sostituito, quindi non è riparabile a posteriori: riesporta il file in UTF-8, oppure incolla il testo invece di trascinare il file, perché il testo negli appunti è già stato decodificato dall'applicazione che lo possiede.
Un ultimo caso di codifica ha il filo tagliente: UTF-16 senza byte order mark. Non c'è nulla da fiutare, quindi il file viene letto come UTF-8, un byte su due è uno zero, e quello che torna è un unico oggetto la cui chiave contiene caratteri NUL. Sembra spazzatura e non testo leggermente sbagliato, e questo è l'esito buono: te ne accorgi subito. I fallimenti pericolosi sono quelli silenziosi, e Windows-1252 letto come UTF-8 è il più silenzioso di tutti, perché le colonne combaciano alla perfezione e solo le lettere accentate sono sbagliate.
Il sesto caso che nessuno elenca: la riga sep=
Excel accetta una prima riga nella forma sep=; come istruzione su quale carattere separa i campi, e molte routine di esportazione la emettono perché un file a punto e virgola si apra bene in un lettore il cui separatore di elenco è la virgola. Non è nella RFC 4180 e non lo è mai stata: è una convenzione di fornitore diffusasi perché funziona. Per un convertitore che non ne ha mai sentito parlare, è semplicemente il primo record del file.
È esattamente ciò che succede qui. Dai al convertitore sep=; seguito da Name;Ville;Montant e due righe di dati: il rilevamento automatico sceglie correttamente il punto e virgola — perché la riga sep= ne contiene uno — ma il passo dell'intestazione se la mangia. Ottieni tre oggetti invece di due, con le chiavi "sep=", column2 e column3, e i veri nomi di colonna Name, Ville e Montant compaiono come valori del primo. È evidente appena guardi l'output, e invisibile se lo inoltri direttamente altrove. Cancella la prima riga prima di convertire, oppure converti prima il delimitatore e lascia che il convertitore di delimitatore riscriva il suggerimento per te.
| Input | Cosa esce | Perché |
|---|---|---|
| Tom,"Paris, France" | Due campi: Tom e Paris, France | Il parser tiene uno stato «tra virgolette»; un delimitatore citato è dato |
| Tom, "Paris, France" (spazio dopo la virgola) | Tre campi: Tom, " Paris e France" | RFC 4180 sezione 2.4: lo spazio fa parte del campo, quindi la virgoletta non apre nulla |
| 0044 con la conversione dei tipi attiva | La stringa "0044" | Number("0044") stampa 44, che non è ciò che è arrivato, quindi la cella resta com'è |
| TRUE con la conversione dei tipi attiva | La stringa "TRUE"; solo true minuscolo diventa booleano | Il test è un confronto letterale con le due parole chiave JSON, che sono minuscole |
| name,name,name sopra a,b,c | Chiavi name, name_2 e name_3 — tutti e tre i valori conservati | Una chiave ripetuta in un oggetto distrugge dati: la seconda e la terza vengono rinominate |
| Un file Windows-1252 trascinato nello strumento | Andr� e K�ln, analizzati senza intoppi e senza avviso | Nessun selettore di codifica: il lettore assume UTF-8 e sostituisce ogni byte non valido |
| Un file che inizia con sep=; | Il punto e virgola è rilevato bene, ma sep= diventa la prima chiave e l'intestazione vera diventa una riga di dati | Il suggerimento è una convenzione Excel, estranea a qualsiasi definizione di CSV: il parser lo legge come record |
Domande frequenti
- Conviene attivare la conversione dei tipi o lasciarla spenta?
- Lasciala spenta, a meno che qualcosa a valle non richieda numeri veri. Una stringa è una rappresentazione senza perdita di ciò che c'era nella cella; un numero è una rappresentazione con perdita, e la perdita è irreversibile. La protezione di andata e ritorno fa sì che proprio questo strumento non rovini 0044, 1.0 né un identificatore a 19 cifre, ma convertirà una colonna di codici postali privi di zero iniziale: 75001 e 75008 diventano numeri mentre un codice olandese come 1012 AB resta stringa — una colonna, due tipi, e chi consuma il JSON deve gestirli entrambi. Se ti servono numeri, converti dopo le colonne che ti interessano, dove puoi nominarle, invece di lasciare che un'euristica decida colonna per colonna.
- Come fa il convertitore a sapere che il mio file usa i punti e virgola?
- Conta i delimitatori candidati — virgola, punto e virgola, tab e barra verticale — solo sul primo record, saltando ciò che sta tra virgolette, e prende il più frequente. Leggere solo il primo record è voluto: un'intestazione come "Nom;Prénom" non deve essere valutata da una virgola sepolta in un indirizzo citato trecento righe più giù. Il limite ne è lo specchio. Se la tua intestazione contiene una virgola e le righe di dati usano i punti e virgola, il rilevatore sceglie la virgola e ogni riga diventa un unico campo. Due sintomi lo tradiscono all'istante: una sola chiave per oggetto, e una chiave il cui nome è l'intera riga di intestazione. Nel dubbio, imposta il delimitatore esplicitamente invece di affidarti al rilevamento.
- Perché i miei caratteri accentati sono diventati punti interrogativi o rombi neri?
- Perché il file non era UTF-8 e nulla lo ha detto al lettore. Il rombo nero con punto interrogativo è U+FFFD, il carattere di sostituzione Unicode, ed è ciò che un decodificatore emette quando una sequenza di byte non è valida nella codifica che gli è stata imposta. Il tuo file era quasi certamente Windows-1252 o ISO 8859-1, dove é è il singolo byte E9; UTF-8 ha bisogno di due byte per é, ed E9 da solo non è l'inizio legale di nulla. Il danno avviene prima che giri il parser CSV, quindi nessuna impostazione CSV lo annullerà. Apri l'originale in un editor che ti faccia scegliere la codifica, salvalo come UTF-8 e riconverti. Se il file viene da un foglio di calcolo, esportalo con l'opzione UTF-8 invece che come CSV semplice.
- Le mie righe non hanno tutte lo stesso numero di campi. Perderò dei dati?
- No. La larghezza dell'output è quella della riga più larga del file, comprese le righe più larghe dell'intestazione. Una riga corta riceve stringhe vuote per le colonne mancanti; una riga lunga riceve chiavi extra chiamate column4, column5 e così via per i campi che l'intestazione non ha mai nominato. Nulla viene troncato, e questo conta: una riga troppo lunga è di solito il sintomo di un delimitatore non protetto più in alto, e troncare cancellerebbe la prova. Se vedi column4 nel tuo JSON e l'intestazione aveva solo tre nomi, cerca un campo con un delimitatore non citato — è lì che il file è andato storto.
- Esiste una versione di CSV senza questi problemi?
- Dentro del CSV stesso no, perché il formato non ha dove mettere i metadati che risolverebbero le questioni. Ciò che esiste sono convenzioni sovrapposte: un file di schema che accompagna e nomina le colonne e i loro tipi, un profilo di esportazione fisso concordato fra i due sistemi, o un formato che porta con sé i propri tipi. Se controlli entrambe le estremità, JSON Lines — un oggetto JSON per riga — risolve in un colpo virgolette, a capo e tipi, al prezzo di un file più grande che non si apre in un foglio di calcolo. Se non controlli entrambe le estremità, la risposta pratica è essere noiosi: UTF-8 con BOM, virgola o punto e virgola in modo costante, tutti i campi citati, nessuna riga sep=, e un'intestazione con nomi unici e privi del delimitatore.
Articoli che potrebbero interessarti
Tutte le guide →Strumenti correlati
Questo descrive ciò che questi convertitori fanno oggi, verificato eseguendoli, e non ciò che una norma imponga a un convertitore. Il CSV non ha una norma prescrittiva: la RFC 4180 è informativa e descrive una prassi diffusa, perciò due strumenti apparentemente corretti possono divergere sullo stesso file senza che nessuno sbagli. L'appiattimento, il riconoscimento dei tipi e quello degli array sono convenzioni, non regole. Prima di convertire dati che non potrai riesportare, passa prima su una copia e confronta il numero di righe e colonne alle due estremità.
Fonti
- IETF — RFC 4180, Common Format and MIME Type for Comma-Separated Values (CSV) Files, October 2005 — Informational, not a standard: section 2 rules 5 to 7 on double quotes, section 2.4 on spaces being part of a field, and the header and charset parameters of the text/csv media type
- IETF — RFC 8259, The JavaScript Object Notation (JSON) Data Interchange Format, December 2017 — section 4 on object member names being unordered and the consequences when names repeat, and section 6 on the interoperability limits of numbers
- WHATWG — Encoding Standard — the decoder algorithms, the BOM sniffing rules, and the use of U+FFFD as the replacement for a byte sequence that is not valid in the chosen encoding
- W3C — File API — the read operation and its encoding determination: an explicit encoding, then the blob's charset parameter, then a byte order mark, then UTF-8 as the fallback
Hai notato un errore in questo articolo?