Hranice převodu JSON ↔ YAML a XML
Shrnutí: Tvzení, že JSON je podmnožinou YAML, zaručuje jen jeden směr a XML nemá žádné oficiální mapování do JSON – všechno stojí na konvenci.

Stručně: používejte JSON jako zprostředkující formát: zdroj nejprve parsujte do datového modelu a poté serializujte do cíle; před převodem rozhodněte, zda musí přežít komentáře, atributy a jmenné prostory XML a číselné či booleovské typy.
Zda je převodní pipeline použitelná, nezávisí na tom, jak hezký je výstup, ale na tom, zda si uvědomujete, kolik toho zahodila.
1. JSON a YAML: jednostranné zahrnutí
Specifikace YAML 1.2 to říká přímo: JSON je podmnožina YAML. Každý platný JSON je tedy platný YAML:
{"name": "json", "tags": ["a", "b"], "count": 3}
Tento úryvek je zároveň JSON i YAML v proudovém stylu. Opak neplatí.
| Vlastnost | JSON | YAML 1.2 |
|---|---|---|
| Komentáře | ✗ | ✓ |
| Kotvy a aliasy | ✗ | ✓ |
Více dokumentů (---) |
✗ | ✓ |
| Neřetězcové klíče | ✗ | ✓ (1: a, true: b) |
| Složené klíče | ✗ | ✓ (? [a, b]) |
| Citlivost na odsazení | ✗ | ✓ (odsazení tabulátorem je odmítnuto) |
| Odvozování typů | explicitní | implicitní (0o17, 0x1F, .inf) |
| Čitelnost jednoho řádku | dobrá | špatná (dlouhý obsah se spoléhá na syntaxi skládání) |
1.1 Odvozování typů je největší past
YAML parsuje některé hodnoty, které vypadají jako řetězce, do jiných typů:
zip: 01234 # řetězec nebo osmičkové celé číslo? jádrové schéma YAML 1.2 to považuje za řetězec
version: 1.10 # 1.1 (desetinné číslo); sémantika se ztratí
country: NO # v YAML 1.2 řetězec; v YAML 1.1 boolean false
date: 2026-10-30 # některé implementace to parsují jako objekt data
Příklad s NO už leckoho spálil: s parserem YAML 1.1 (některé starší knihovny) se country: NO stane false.
Protiopatření: každý identifikátor, který „vypadá jako číslo nebo boolean“, dejte do uvozovek, například version: "1.10".
1.2 Kotvy, aliasy a více dokumentů
defaults: &defaults
retries: 3
timeout: 30
serviceA:
<<: *defaults
host: a.example.com
---
serviceB:
<<: *defaults
host: b.example.com
Po expanzi kotev a převodu do JSON obvykle skončíte se dvěma duplikovanými konfiguračními objekty, nebo s jedním polem (více dokumentů se musí stát [{...}, {...}]).
V obou případech se kotvy při zpětném převodu neobnoví – tato ztráta je nevyhnutelná.
1.3 Převod, který zachovává komentáře
Většina knihoven komentáře při převodu prostě zahodí. Když je potřebujete udržet, použijte Document API balíčku yaml:
import { parseDocument } from 'yaml';
const document = parseDocument(`
# Konfigurace služby
port: 8080
`);
document.set('port', 3000);
console.log(document.toString());
// # Konfigurace služby
// port: 3000
Test je jednoduchý: pokud bude tento soubor lidmi znovu a znovu upravován, neberte cestu „parse → objekt → serializace“.
1.4 Jak ověřit konzistenci obousměrného převodu
/**
* Ověří konzistenci obousměrného převodu JSON → YAML → JSON.
* @param sourceValue Původní hodnota JSON
* @param stringify Funkce pro serializaci YAML
* @param parse Funkce pro parsování YAML
* @returns Zda jsou oba výsledky přesně shodné
*/
function isRoundTripStable(
sourceValue: unknown,
stringify: (input: unknown) => string,
parse: (input: string) => unknown,
): boolean {
const yamlText = stringify(sourceValue);
const restored = parse(yamlText);
return JSON.stringify(restored) === JSON.stringify(sourceValue);
}
Spustit ho na skutečných datech má větší cenu než desetkrát číst dokumentaci. Zajistěte, aby sada vzorků pokrývala: null, prázdný řetězec, prázdné pole, "00", "1e5", zápornou nulu, celá čísla větší než 2^53 a dlouhé řetězce obsahující zalomení řádku.
2. JSON a XML: žádné standardní mapování neexistuje
XML je mnohem starší než JSON a má podstatně silnější strukturální možnosti (atributy, jmenné prostory, smíšený obsah, DTD). Mezi oběma formáty neexistuje žádná oficiální specifikace mapování; každý nástroj dělá totéž: zvolí konvenci a pak se snaží moc neztratit.
2.1 Běžné konvence
| Konvence | Reprezentace | Příklad |
|---|---|---|
| Prvek → klíč objektu | Stejnojmenný prvek se stane polem | <a>1</a> → {"a": "1"} |
Atribut → prefix @ |
@name |
<a id="1"/> → {"a": {"@id": "1"}} |
Text → #text |
Když existuje atribut i text | <a id="1">hi</a> → {"a": {"@id": "1", "#text": "hi"}} |
| Opakovaný prvek → pole | Dva a více výskytů se stanou polem | <i>1</i><i>2</i> → {"i": ["1","2"]} |
| Jmenný prostor → prefix | ns:tag |
<ns:tag/> → {"ns:tag": ...} |
<order id="A1001">
<item sku="X1" qty="2">笔记本</item>
<item sku="X2" qty="1">钢笔</item>
</order>
{
"order": {
"@id": "A1001",
"item": [
{ "@sku": "X1", "@qty": "2", "#text": "笔记本" },
{ "@sku": "X2", "@qty": "1", "#text": "钢笔" }
]
}
}
2.2 Pět nevyhnutelných ztrát
- Pořadí prvků: objekty JSON jsou neuspořádané, zatímco XML je uspořádaná posloupnost prvků;
- Atributy vs. podřízené prvky: v XML nesou odlišnou sémantiku, ale v JSON se obojí stane obyčejnými poli;
- Smíšený obsah: struktury jako
<p>你好 <b>世界</b> 再见</p>nelze vyjádřit prostým objektem; - Deklarace jmenných prostorů:
xmlnsje koncept rozsahu platnosti; po zploštění do polí je rozsah pryč; - CDATA a entity:
<![CDATA[...]]>a&nejsou v JSON ničím víc než textem.
2.3 Dilema „jeden prvek, nebo pole“
<list><item>1</item></list>
Při jediném item vrátí xml2js ve výchozím nastavení objekt a pole vrátí až od dvou výskytů.
Výsledkem je, že frontendový kód musí psát Array.isArray(x) ? x : [x].
Protiopatření: při parsování zapněte volbu „vždy pole“, nebo pole deklarujte jako typ pole podle schématu.
import { parseStringPromise } from 'xml2js';
const parsed = await parseStringPromise(xmlText, {
explicitArray: true, // vrátit pole i pro jediný prvek
trim: true,
explicitRoot: false,
});
2.4 Bezpečnostní připomenutí
Před parsováním nedůvěryhodného XML musíte vypnout externí entity, jinak můžete spustit XXE (čtení lokálních souborů, skenování vnitřní sítě):
// V ekosystému Node se DTD a externí entity obvykle vypínají pomocí voleb parseru
const parserOptions = { doctype: false, externalEntities: false };
Převod XML → JSON je jen první vrstva; převod neodstraňuje útočnou plochu, kterou XML přináší.
3. Kam který ze tří formátů patří
| Scénář | Doporučený formát | Důvod |
|---|---|---|
| Veřejná HTTP API | JSON | Nativně podporovaný prohlížeči i každým jazykem |
| Ručně udržovaná konfigurace | YAML | Komentáře, odsazení, čitelnost |
| Dokumenty vyžadující silné schéma a jmenné prostory (SOAP, Office Open XML, SVG) | XML | Strukturální možnosti a existující ekosystém |
| Interakce s provozními nástroji (K8s, CI) | YAML | Výchozí formát ekosystému |
| Persistence, cache, logy | JSON / JSONL | Univerzální a bez závislostí |
4. Poznámky k implementaci
import { stringify as toYaml } from 'yaml';
/**
* Převede hodnotu JSON na text YAML a přitom ponechá čínské znaky neescapované.
* @param value Hodnota JSON k převodu
* @returns Text YAML
*/
function jsonToYaml(value: unknown): string {
return toYaml(value, {
indent: 2,
lineWidth: 0, // vypnout automatické zalamování, aby se dlouhé řetězce nelámaly
defaultStringType: 'QUOTE_DOUBLE',
});
}
Několik praktických voleb:
lineWidth: 0vypne automatické zalamování; jinak se dlouhá URL láme do více řádků a špatně se čte;- citování všeho dvojitými uvozovkami zabrání chybnému čtení hodnot jako
NOa1.10; - u dat s citlivou číselnou přesností převeďte hodnoty na řetězce ještě před serializací.
Pro směr XML pevně stanovte jednu mapovací funkci a „prefix atributu“, „název textového klíče“ a „strategii polí“ dejte do jejího podpisu, místo abyste je rozptylovali kolem:
type XmlMappingOptions = {
attributePrefix: string;
textKey: string;
alwaysArray: boolean;
};
const defaultMapping: XmlMappingOptions = {
attributePrefix: '@',
textKey: '#text',
alwaysArray: false,
};
5. Kontrolní seznam
- [ ] Každý identifikátor v YAML, který „vypadá jako číslo/boolean“, je v uvozovkách;
- [ ] Sada validačních vzorků pro JSON → YAML → JSON je definována a pokryta testy;
- [ ] Když se komentáře musí zachovat, používá se Document API, nikoli obousměrný převod přes objekt;
- [ ] Konvence mapování XML je zapsaná a tým sdílí jednu sadu parametrů;
- [ ] Při parsování nedůvěryhodného XML jsou DTD a externí entity vypnuté;
- [ ] Počet bajtů a polí se před převodem a po něm porovnává a každá anomálie spustí alert.
Časté dotazy
Jak mám volit směr převodu mezi JSON, YAML a XML?
Preferujte JSON jako zprostředkující formát: nejprve parsujte YAML a XML do datového modelu a poté serializujte z tohoto modelu, místo abyste přepisovali text. Když musí komentáře přežít, použijte Document API YAML a jednu konvenci mapování XML pevně stanovte dřív, než budete potřebovat atributy nebo jmenné prostory.
Proč převod XML do JSON ztrácí atributy a jmenné prostory?
JSON nemá koncept atributů ani jmenných prostorů, takže atributy XML, prefixy a URI jmenných prostorů je nutné složit do obyčejných klíčů, obvykle s prefixem @ nebo uzlem #text. Jakmile se pravidla mapování rozcházejí, stejné XML produkuje různý JSON a nelze ho obousměrně převést.
Mám udržovat JSON, YAML a XML v dlouhodobé obousměrné synchronizaci?
Ne. Komentáře, kotvy, atributy XML a jmenné prostory jsou mezi formáty ztrátové a obousměrná synchronizace neustále hromadí rozdíly. Zvolte jeden zdrojový formát a ostatní považujte za jednorázové exporty nebo dočasné náhledy.