Vad som går förlorat vid konvertering mellan JSON, YAML, CSV och XML
Varje konvertering mellan dataformat tappar det som källan kunde uttrycka och destinationen inte kan. Vissa förluster är uppenbara: kommentarer försvinner i samma ögonblick som YAML blir JSON. De flesta är tysta. Ett datum anländer som en sträng, en array blir fem kolumner, ett ID som slutade på 3 slutar nu på 2. Den nyttiga vanan är att veta vilken detalj som kommer att försvinna innan den gör det.
Vad varje format kan bära
| Kommentarer | Typer | Nästling | Datum | Nyckelordning | Dubblettnycklar | |
|---|---|---|---|---|---|---|
| JSON | Nej | string, number, boolean, null, object, array | Ja | Ingen inbyggd typ | Oordnade enligt specen, bevaras av de flesta parsrar | Odefinierat, den sista vinner oftast |
| YAML | Ja, # | JSON:s, plus tidsstämplar och mer i 1.1 | Ja | Ja i 1.1, taggberoende i 1.2 | Bevaras i filen, inte efter tolkningen | Ogiltiga, men accepteras ofta |
| CSV | Nej | Inga, varje cell är text | Nej, bara platta rader | Nej | Bestäms av rubrikraden | Tillåtna och tvetydiga |
| XML | Ja, <!-- --> | Inga utan ett schema, typade med XSD | Ja | Bara genom ett schema | Element är ordnade, attribut inte | Upprepade element är normalt, dubblerade attribut är ett fel |
| TOML | Ja, # | string, integer, float, boolean, date, time, date-time, array, table | Ja, via tabeller | Ja, förstklassigt | Inte signifikant | Ett fel, alltid |
Två kolumner orsakar de flesta överraskningarna. Typer avgör om 007 överlever eller anländer som talet 7, och nästling avgör om en konvertering till CSV är möjlig utan att en konvention uppfinns.
CSV är platt, och varje cell är text
CSV har en enda form: rader av fält. Det finns inget sätt att uttrycka ett objekt i en cell, så en konverterare måste hitta på något. Oftast plattar den ut med punktseparerade sökvägar, så att detta:
[{ "id": 1, "name": { "first": "Ada" }, "tags": ["admin", "ops"] }]
blir:
id,name.first,tags.0,tags.1
1,Ada,admin,ops
Det går att bygga tillbaka utan problem tills två saker inträffar. Arrayer av olika längd breddar rubriken till den längsta posten, så en rad med tre taggar lägger till en tags.2-kolumn och lämnar en tom cell på alla andra rader. Och en nyckel som redan innehåller en punkt går efteråt inte att skilja från en nästlad sökväg, så resan tillbaka bygger upp fel struktur. Alternativet är en JSON-sträng i cellen, "[""admin"",""ops""]" med citattecknen dubblerade som CSV kräver, vilket inte förlorar något och som ingenting längre fram förstår.
Typerna är den andra halvan. En cell håller tecken, så en läsare måste gissa. Att gissa ger 007 som 7, 1.0 och 1 som ett och samma värde, ett postnummer utan sin inledande nolla, en företagskod TRUE som en boolean. Att inte gissa ger varje tal som en sträng. Typinferensen i CSV till JSON, eller i TSV till JSON, är en gissning med förnuftiga standardval snarare än en återställning, eftersom de ursprungliga typerna aldrig skrevs ned.
En tom cell kan betyda tom sträng, null, noll eller ej tillämpligt; bestäm vad den betyder och säg det, för filen kan inte. Att byta avgränsare, som CSV till TSV gör, är den enda säkra konverteringen här: det finns inga typer att skada.
YAML gissar typen åt dig
YAML härleder typer ur ociterad text, vilket är det som gör formatet trevligt att skriva och riskabelt att konvertera.
country: no
version: 1.20
build: 010
start: 12:30
Under en YAML 1.1-parser, vilket är vad PyYAML och många äldre bibliotek fortfarande implementerar, är det:
{ "country": false, "version": 1.2, "build": 8, "start": 750 }
Alla fyra bytte betydelse. no är Norge-problemet: YAML 1.1 behandlar y, yes, no, on och off som booleaner, så landskoden blir false. 1.20 är ett flyttal, så den avslutande nollan är borta. 010 matchar det oktala mönstret och blir 8. 12:30 matchar det sexagesimala mönstret och blir 750, ett antal minuter.
En parser med YAML 1.2:s kärnschema, som nuvarande js-yaml, läser samma fil som strängen "no", 010 som 10 och "12:30" som en sträng. En fil, två parsrar, olika data. Specifikationerna är genuint oense; 1.2 slopade reglerna om sexagesimala tal och boolean-ord med flit.
Försvaret är ett enda tecken. Citera allt som är avsett som text: country: "no", version: "1.20". Konverteraren mellan YAML och JSON visar vad en parser gjorde av en fil, eftersom JSON inte har någonstans att gömma tvetydigheten.
JSON har inga datum, inga kommentarer och ett tak på 53 bitar
JSON-tal är IEEE 754-dubbelprecisionstal, så heltal förblir exakta bara upp till 2^53, vilket är 9007199254740992. Bortom det:
{ "id": 9007199254740993 }
Tolka och serialisera om det i de flesta språk och det kommer tillbaka som 9007199254740992. Värdet går inte att representera alls, så det är inte så mycket avrundat som otillgängligt. Det är därför Discord, Twitter och andra snowflake-API:er skickar ID som strängar: ett 64-bitars heltal ryms inte i ett JSON-tal.
Det finns ingen datumtyp heller, så datum färdas som strängar, konventionellt ISO 8601, och ingenting markerar "2026-08-25" som ett datum snarare än text. Genom TOML eller en YAML 1.1-parser kan det bli ett riktigt datum och sedan komma tillbaka formaterat annorlunda. JSON har inte heller några kommentarer och inga avslutande komman, så anteckningar i en YAML- eller TOML-källa är borta snarare än flyttade. Att generera TypeScript-gränssnitt från ett stickprov ärver båda blinda fläckarna: en sträng är inte ett datum, och ett saknat fält är inte valfritt.
XML får inte plats i JSON, i någon riktning
XML skiljer attribut från underelement, och JSON har bara nycklar.
<user id="7" active="true">
<name>Ada</name>
<tag>admin</tag>
<tag>ops</tag>
</user>
En konverterare väljer en konvention, oftast att prefixa attribut med @ och slå ihop upprepade element till en array. Det är förlustbehäftat på ett specifikt sätt: med ett enda <tag> finns ingen array, så utdatas form beror på datan snarare än på schemat, och kod som förväntar sig en lista går sönder på den post som har en enda tagg. Blandat innehåll som <p>Hello <b>there</b> friend</p> har ingen motsvarighet i JSON, och namnrymder, CDATA och kommentarer försvinner.
Den omvända riktningen förlorar andra saker. Elementnamn kan inte börja med en siffra eller innehålla mellanslag, så godtyckliga JSON-nycklar måste förvanskas. null blir ett tomt element eller xsi:nil enligt konvention. En array på toppnivå behöver ett omslutande element som aldrig fanns i källan. Och ingenting i JSON registrerar om ett värde en gång var ett attribut, så JSON till XML producerar bara element. CSV till XML gör det valet öppet och frågar efter element eller attribut.
Nyckelordning och dubblettnycklar
Nyckelordning är garanterad ingenstans och bevarad nästan överallt. JSON-specifikationen kallar objekt oordnade, ändå behåller varje etablerad parser insättningsordningen. Att sortera nycklarna är ändå säkrare för allt som jämförs eller kontrollsummeras, eftersom en diff av två dokument vars nycklar bara har flyttat sig är oläslig. Att jämföra strukturellt, som JSON-jämförelseverktyget gör, matchar på nyckel snarare än på position och undviker frågan.
Dubblettnycklar är den vassare kanten. JSON säger ingenting, så parsrarna skiljer sig åt och de flesta behåller tyst det sista värdet. YAML förbjuder dem och många parsrar accepterar dem ändå. TOML avvisar dem rakt av. CSV tillåter två kolumner med samma rubrik och överlåter åt läsaren att avgöra, vilket är skäl nog att först köra en CSV-validerare över en obekant fil. XML är den enda där upprepning är meningsbärande snarare än en olyckshändelse.
Att konvertera med avsikt
Varje kedja har ett förlustbehäftat steg. Välj vilket det är i stället för att upptäcka det senare.
- Nå det smalaste formatet sist. XML till JSON till CSV förlorar attribut i första steget och nästling i det andra. Om CSV är destinationen, bestäm vilka fält som spelar roll och platta ut medvetet.
- Citera före konverteringen, inte efter. Versionsnummer, landskoder, postnummer och allt med en inledande nolla hör hemma inom citattecken i källan. När
noväl ärfalsegår originaltexten inte att återskapa. - Kontrollera utdata som data. Formatera den, bekräfta antalet poster och inspektera en känt besvärlig rad: kommat i ett namn, den längsta arrayen, det största ID:t.
- Diffa en tur och retur. Konvertera ut, konvertera tillbaka, jämför. Det jämförelsen rapporterar är vad konverteringen inte kan bära.
- Behåll originalet. Den konverterade filen är ett derivat, och när någon om tre månader frågar om ett fält var tomt eller saknades är det bara källan som kan svara.