JSON Toolkit

JSON na CSV: odkud se bere hlavička a proč se mění hodnoty

Shrnutí: Devět experimentů na skutečném konvertoru: strategie „klíče z prvního záznamu" potichu ztrácí pole, null, absence i prázdný řetězec se stanou všichni prázdnými buňkami, 20místné celé číslo ztratí přesnost už v JSON.parse a tečka v klíči srazí dva různé cesty do jednoho sloupce. Jak se hlavička staví, které hodnoty se deformují a jak zvolit strategii podle toho, co s daty bude dále.

Pokročilý~6 min čteníPublikováno 2026-11-04Aktualizováno 2026-11-04
Strategie hlaviček a věrnost hodnot při konverzi JSON na CSV

Když konverze JSON na CSV ztrácí data, skoro nikdy se to nestane během kroku „konverze". Ztráta vzniká ve dvou otázkách, které si nikdo nepoložil: kdo rozhodl o hlavičce a zůstává každá buňka rozpoznatelná jako ta hodnota, kterou byla?

Pole zmizí za třicet sekund

Mnoho konvertorů — včetně idiomatické pipeline jq — bere hlavičku pouze z prvního záznamu. Reprodukujte důsledky v Pythonu:

data = [{"id": 1, "name": "A"}, {"id": 2, "email": "b@x.com"}]
first_keys = list(data[0].keys())          # ['id', 'name']
rows = [[r.get(k, "") for k in first_keys] for r in data]
# E-mail druhého záznamu zmizí bez chyby i varování

Klíč email není v hlavičce: nemá ani právo stát se prázdnou buňkou — sloupec nikdy nevznikl a hodnota je potichu vyhozena. Oprava je sjednocení klíčů: projít všechny záznamy a vytvořit sloupec pro každý klíč, který se kdekoliv objeví. Tato samá data přes sjednocení dají hlavičku id,name,email, s prázdnou buňkou tam, kde chybí name — a nic se neztratí.

Přímý konvertor na naší stránce /convert/csv používá přesně tuto strategii. Pro vstup [{"id":1,"name":"A"},{"id":2,"email":"b@x.com"}] dává:

id,name,email
1,A,
2,,b@x.com

Klíče jsou seřazené podle prvního výskytu a chybějící pole se stanou prázdnými buňkami. jsonoperations i jsonbeam obě tomu říkají „oprava klasického bugu" — ale „používá sjednocení" je jen polovina příběhu. Sjednocení má svou vlastní cenu; to je další sekce.

Tři způsoby, jak postavit hlavičku — a komu která sedí

Strategie hlavičky Jak vzniká Síla Cena
Klíče z prvního záznamu Čte se jen první záznam Málo sloupců, stabilní, jeden řádek jq Nová pole dalších záznamů se potichu ztrácejí
Sjednocení klíčů Čtou se všechny záznamy Žádné pole se neztratí Řídké sloupce a mnoho prázdných, když se tvary liší
Explicitní seznam sloupců Definovaný ručně nebo schématem Plně ovladatelný, reprodukovatelný Vyžaduje údržbu; nová pole vyžadují ruční aktualizaci

Kritériem výběru není „které je sofistikovanější", ale co příjemce s tímto CSV udělá:

  • Kolega, který se jen podívá: klíče z prvního záznamu stačí, tvary záznamů bývají obvykle jednotné.
  • Report nebo datová pipeline: sjednocení klíčů je minimum — prázdná buňka je sama o sobě informace.
  • Opakovaný automatický export: explicitní seznam sloupců. Drift tvaru (přidané pole nahoře, přejmenovaný klíč) musí spadnout hlasitě, ne potichu vyrábět tabulku, kterou si nikdo nevšimne, že je špatná.

Třetí možnost je nejvíce opomíjená. V automatizaci jsou „konverze proběhla úspěšně" a „konverze je správná" dvě různá tvrzení — a explicitní seznam sloupců je jediný mechanismus, který je oddělí.

Když klíč obsahuje tečku, cesty zploštění kolidují

Zploštění v tečkové notaci změní {"a": {"b": 2}} na sloupec a.b. Ale co když klíč sám obsahuje tečku? Otestováno v pandas:

import pandas as pd
d = [{"a.b": 1, "a": {"b": 2}}]
pd.json_normalize(d, sep=".")
# Přežije jen jeden sloupec: ['a.b'] — dva úplně různé klíče kolidují na téže cestě

Literální klíč a.b a vnořená cesta a → b produkují stejný název sloupce, jeden přepíše druhý a data se potichu poškodí. Je to sestrojený příklad, ale u skutečných dat se to děje: názvy metrik monitoringu (cpu.usage) a pole logů (error.type) milují tečky.

Dvě léčby:

  1. Změňte oddělovač. sep="_" nebo vlastní oddělovač — poté, co ověříte, že původní klíče ho nikdy neobsahují.
  2. Změňte notaci cest. JSON Pointer používá / jako oddělovač (/a/b); Excel konvertor toolvore jde touto cestou. Má tutéž třídu dvojznačnosti — klíč s / se zapisuje jako ~1 — ale pravidla escapování jsou normalizovaná v RFC 6901, nevymýšená každým nástrojem zvlášť.

Cokoli si vyberete, zapište pravidlo do dokumentace exportu. Hlavička CSV je jediné „schéma", které tato data nesou; čtenář vaše pravidla zploštění nenajde nikde jinde.

Pole: tři strategie, každá ztrácí něco jiného

Pole jsou tou pravou překážkou na cestě JSON do CSV: buňka nemůže obsahovat více hodnot. Tři strategie se ztrátami ověřenými v praxi:

Strategie Tvar výstupu Co se ztrácí Ideální pro
JSON v buňce Pole serializované jako JSON řetězec v buňce Tabulkové nástroje nemohou prvky filtrovat ani agregovat Věrnost napřed, ruční prohlížení
Indexované sloupce tags.0, tags.1, sloupec pro každý Počet sloupců exploduje s nejdelším polem; mnoho prázdných Malá pole pevné délky
Rozvinuté řádky Řádek na prvek, pole rodiče zduplikovaná Inflace řádků; chování prázdných polí je třeba určit zvlášť Detailní analýza, agregace po prvku

Naše konvertory /convert/csv a /convert/table používají JSON v buňce. Vstup [{"orderId":"A001","items":[{"sku":"X1","qty":2},{"sku":"Y9","qty":1}]}] dává:

orderId,items
A001,"[{""sku"":""X1"",""qty"":2},{""sku"":""Y9"",""qty"":1}]"

Když potřebujete rozvinuté řádky, pandas record_path to udělá přímo:

orders = [{"orderId": "A001", "total": 99,
           "items": [{"sku": "X1", "qty": 2}, {"sku": "Y9", "qty": 1}]}]
items = pd.json_normalize(orders, record_path=["items"],
                          meta=["orderId", "total"], sep=".")
# Dva řádky detailů; orderId a total zduplikované v každém

U rozvinutí jsou tři pasti, které je třeba rozhodnout předem: rozvinout několik polí najednou znamená postavit kartézský součin; prázdná pole ve výchozím nastavení nevydají žádný řádek (váš seznam zákazníků ztratí lidi); a duplikovaná pole rodiče vyžadují deduplikaci, když dále agregujete po orderId. Průvodce convertcsvonline uvádí „nejdřív stanovte granularitu" jako první krok právě proto — jakmile je granularita fixní, všechny tři otázky mají jedinou odpověď.

Otestováno: hodnoty, které z konverze vycházejí změněné

Test věrnosti na našem skutečném konvertoru (Node v22.13.1 + papaparse, stejná implementace jako /convert/csv):

1. null, absence i prázdný řetězec se stanou stejnými prázdnými buňkami. Vstup [{"a":null},{"b":""},{"c":1}] — všechny tři buňky vycházejí prázdné. CSV nemá tři stavy, jen „znaky jsou" a „znaky nejsou". Po round-trip se všechny tři čtou jako ""; původní sémantika je nenávratně pryč.

2. Vnořené objekty a pole se stanou JSON řetězci. Uvozovky se podle RFC 4180 zdvojí ({""name"":""张三""}) a po vložení buňky do jakéhokoli JSON parseru se hodnota vrátí — proto JSON v buňce věrnost zachovává.

3. 20místné celé číslo ztratí přesnost už při parsování — a vina není na CSV. Vstup "id": 9007199254740993; v CSV už je 9007199254740992. JSON.parse v JavaScriptu používá IEEE 754 double: ztráta nastává při parsování, před jakýmkoli CSV konvertorem. Python json.load ji zachová celou. Pole typu identifikátor (snowflake ID, čísla objednávek) by měla od producenta přicházet jako řetězce. Více o přesnosti čísel v proč velká čísla ztrácejí přesnost.

4. Vodicí nuly v řetězcích jsou na textové vrstvě CSV v bezpečí. "phone": "00123" zůstane 00123 v CSV, protože je to řetězec od začátku do konce a nic ho poškrábe. Nuly poškrábe až pozdější krok, kdy Excel bere buňku jako číslo — viz čtyři způsoby, jak převést JSON na Excel.

5. Pole s čárkami, uvozovkami nebo odřádkováním. papaparse drží RFC 4180: pole do uvozovek, vnitřní uvozovky zdvojit. "He said \"hi\", ok" vychází jako "He said ""hi"", ok" a projde round-trip bez škrábnutí. Ručně psané serializátory, co obcházejí knihovnu, selžou skoro vždy právě tam.

Round-trip: co mění přepínač odvození typů

CSV → JSON musí u každé buňky uhodnout typ. Náš konvertor odvození typů ve výchozím stavu vypíná a vrací vše jako řetězce:

[{ "id": "9007199254740992", "phone": "00123" }]

Se zapnutým typeInference se id stane číslem (přesnost už je pryč), zatímco phone zůstane řetězcem — pravidla odvození vylučují tvary s vodicí nulou jako 010, které nejsou legálními číselnými literály, takže řetězec přežije spíš náhodou.

Zapamatujte si tuto asymetrii: vypnuté odvození vás v nejhorším případě stojí typy; zapnuté vás může stát data. Sloupce, které čísla potřebují, se převádějí explicitními definicemi sloupců, nikoli globálním odvozením. Implementace rowsToRecords z našeho dřívějšího článku JSON, CSV a tabulková data je přesně ten princip v kódu.

Vyberte strategii podle příjemce: rozhodovací tabulka

Kdo CSV dostává Strategie hlavičky Zacházení s poli Typy
Kolega, který ho otevře jednou Sjednocení klíčů JSON v buňce Bez odvození
Excel / reporting Sjednocení klíčů JSON v buňce, nebo indexované sloupce při pevné délce Sloupce-identifikátory vynuceně textem
Import do databáze Explicitní seznam sloupců Oddělené tabulky master/detail, nebo rozvinutí Explicitní definice sloupců
BI / agregace Explicitní seznam sloupců Rozvinuté řádky, granularita = nejmenší jednotka analýzy Explicitní definice sloupců
Automatická pipeline Explicitní seznam sloupců JSON v buňce, rozvinutí dále v proudu Odvození vypnuto, konverze po sloupcích

Společný zákon: čím blíž člověku, tím víc lze uvolnit; čím blíž stroji, tím víc musí být explicitního.

Checklist ke zkopírování

  • [ ] Hlavička pochází z úplného sjednocení klíčů, nebo z explicitního seznamu? Pokud ne, umíte dokázat, že žádný záznam neztratil pole?
  • [ ] Můžou klíče obsahovat tečky nebo lomítka? Byla ověřena kolize s oddělovačem zploštění?
  • [ ] Je strategie polí zdokumentovaná? Je rozhodnuto chování prázdných polí?
  • [ ] Pro null, absenci a prázdný řetězec: co si z nich přečte příjemce? Zvolte pravidlo a zafixujte ho.
  • [ ] Cestují sloupce typu identifikátor (ID, čísla objednávek, telefony) jako řetězce od začátku do konce?
  • [ ] Po rozvinutí je počet řádků roven součtu délek polí? Ověřeno výběrem?
  • [ ] Příkladná pole s čárkami, uvozovkami a odřádkováním projdou round-trip bez poškození?
  • [ ] Je globální odvození typů při reimportu vypnuté, s explicitními definicemi pro číselné sloupce?

Teprve když všech osm má jasnou odpověď, je ten CSV „správný" — a nejen „úspěšně zkonvertovaný".

FAQ

Proč konvertor nesplušťuje vnořené objekty do sloupců ve výchozím nastavení?

Zploštění vyvede počet sloupců z kontroly: hluboké vnoření produkuje názvy jako user.address.geo.lat a v kombinaci se sjednocením klíčů přidá jediný záznam jiného tvaru celou hromadu sloupců. JSON řetězce v buňkách zachovávají věrnost a stabilní počet sloupců; tavte explicitně, když potřebujete.

Co dělat, když hlavička obsahuje duplicitní názvy sloupců?

Kolize dvou různých klíčů po zploštění (jako a.b proti vnořenému a→b) se řeší nahoře: změňte oddělovač nebo přejděte na JSON Pointer. U duplicit uvnitř téhož objektu konvertory obvykle ponechají poslední hodnotu; duplicity v hlavičce závisí na implementaci — unparse papaparse nededuplikuje a Excel při otevření duplicity tiše sloučí nebo zahodí. Proto je to v checklistu.

Jsou duplikovaná pole rodiče po rozvinutí bug?

Ne — to je design. Rozvinutý řádek je řádek detailů a řádek detailů se musí dát po klíči zpřipojit k rodičovskému záznamu. Agregujte dále v proudu po klíči rodiče; pokud rozměr znepokojuje, exportujte soubor detailů a samostatnou master tabulku.

Jak ověřím, že moje konverze neztratila žádné pole?

Proveďte audit klíčů: spočítejte sjednocení všech klíčů zdrojového JSON, pak množinu hlaviček sloupců CSV a udělejte rozdíl. Neprázdný rozdíl znamená ztracená pole. V dávkových pipeline zakódujte to jako assert do testů: jakákoli výměna konvertoru, která začne ztrácet pole, automaticky spadne.