Die Grenzen der Konvertierung JSON ↔ YAML und XML
Fazit: Die Behauptung, JSON sei eine Teilmenge von YAML, garantiert nur eine Richtung, und XML hat überhaupt keine offizielle JSON-Abbildung – alles ruht auf Konvention.

Kurz gesagt: Verwenden Sie JSON als Zwischenformat: die Quelle in ein Datenmodell parsen und dann zum Ziel serialisieren; vor dem Konvertieren entscheiden, ob Kommentare, XML-Attribute und Namensräume sowie numerische oder boolesche Typen überleben müssen.
Ob eine Konvertierungspipeline brauchbar ist, hängt nicht davon ab, wie hübsch die Ausgabe aussieht, sondern davon, ob Sie wissen, wie viel sie weggeworfen hat.
1. JSON und YAML: einseitige Enthaltensein
Die YAML-1.2-Spezifikation sagt es klar: JSON ist eine Teilmenge von YAML. Jedes gültige JSON ist also gültiges YAML:
{"name": "json", "tags": ["a", "b"], "count": 3}
Dieses Fragment ist sowohl JSON als auch YAML-Flow-Stil. Das Umgekehrte gilt nicht.
| Merkmal | JSON | YAML 1.2 |
|---|---|---|
| Kommentare | ✗ | ✓ |
| Anker und Aliase | ✗ | ✓ |
Mehrere Dokumente (---) |
✗ | ✓ |
| Nicht-String-Schlüssel | ✗ | ✓ (1: a, true: b) |
| Komplexe Schlüssel | ✗ | ✓ (? [a, b]) |
| Einrückungsempfindlich | ✗ | ✓ (Tabulatoreinrückung wird abgelehnt) |
| Typinferenz | explizit | implizit (0o17, 0x1F, .inf) |
| Lesbarkeit in einer Zeile | gut | schlecht (langer Inhalt braucht Faltsyntax) |
1.1 Typinferenz ist die größte Falle
YAML parst manche Werte, die wie Strings aussehen, in andere Typen:
zip: 01234 # string or octal integer? the YAML 1.2 core schema treats it as a string
version: 1.10 # 1.1 (a float); the semantics are lost
country: NO # a string in YAML 1.2; boolean false in YAML 1.1
date: 2026-10-30 # some implementations parse it as a date object
Das NO-Beispiel hat viele verbrannt: Mit einem YAML-1.1-Parser (manche ältere Bibliotheken) wird country: NO zu false.
Gegenmaßnahme: jede Kennung quotieren, die „wie eine Zahl oder ein Boolean aussieht“, etwa version: "1.10".
1.2 Anker, Aliase und mehrere Dokumente
defaults: &defaults
retries: 3
timeout: 30
serviceA:
<<: *defaults
host: a.example.com
---
serviceB:
<<: *defaults
host: b.example.com
Nach dem Expandieren der Anker und der Konvertierung nach JSON endet man üblicherweise mit zwei duplizierten Konfigurationsobjekten oder mit einem Array (mehrere Dokumente müssen [{...}, {...}] werden).
So oder so, Anker werden bei der Rückkonvertierung nicht wiederhergestellt – dieser Verlust ist unvermeidbar.
1.3 Kommentare erhaltende Konvertierung
Die meisten Bibliotheken werfen Kommentare bei der Konvertierung einfach weg. Müssen sie bleiben, verwenden Sie die Document-API des yaml-Pakets:
import { parseDocument } from 'yaml';
const document = parseDocument(`
# Service configuration
port: 8080
`);
document.set('port', 3000);
console.log(document.toString());
// # Service configuration
// port: 3000
Der Test ist einfach: Wird diese Datei immer wieder von Menschen bearbeitet, nehmen Sie nicht den Weg „parsen → Objekt → serialisieren“.
1.4 Wie man Roundtrip-Konsistenz prüft
/**
* Verifies round-trip consistency for JSON → YAML → JSON.
* @param sourceValue Original JSON value
* @param stringify YAML serialization function
* @param parse YAML parsing function
* @returns Whether the two results are exactly equal
*/
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);
}
Ihn gegen echte Daten laufen zu lassen ist mehr wert, als die Dokumentation zehnmal zu lesen. Stellen Sie sicher, dass Ihr Beispielset abdeckt: null, den leeren String, das leere Array, "00", "1e5", negativ null, Ganzzahlen größer als 2^53 und lange Strings mit Zeilenumbrüchen.
2. JSON und XML: es gibt keine Standardabbildung
XML ist weit älter als JSON und hat viel stärkere strukturelle Fähigkeiten (Attribute, Namensräume, gemischter Inhalt, DTD). Zwischen beiden gibt es keine offizielle Abbildungsspezifikation; jedes Werkzeug macht dasselbe: eine Konvention wählen und dann versuchen, nicht viel zu verlieren.
2.1 Übliche Konventionen
| Konvention | Darstellung | Beispiel |
|---|---|---|
| Element → Objektschlüssel | Ein gleichnamiges Element wird ein Feld | <a>1</a> → {"a": "1"} |
Attribut → @-Präfix |
@name |
<a id="1"/> → {"a": {"@id": "1"}} |
Text → #text |
Wenn sowohl Attribut als auch Text vorliegen | <a id="1">hi</a> → {"a": {"@id": "1", "#text": "hi"}} |
| Wiederholtes Element → Array | Zwei oder mehr Vorkommen werden ein Array | <i>1</i><i>2</i> → {"i": ["1","2"]} |
| Namensraum → Präfix | 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 Fünf unvermeidbare Verluste
- Elementreihenfolge: JSON-Objekte sind ungeordnet, XML ist eine geordnete Folge von Elementen;
- Attribute vs. Kindelemente: Sie tragen in XML unterschiedliche Semantik, werden in JSON aber beide einfache Felder;
- Gemischter Inhalt: Strukturen wie
<p>你好 <b>世界</b> 再见</p>lassen sich nicht mit einem einfachen Objekt ausdrücken; - Namensraumdeklarationen:
xmlnsist ein Gültigkeitsbereichskonzept; einmal in Felder abgeflacht, ist der Bereich weg; - CDATA und Entities:
<![CDATA[...]]>und&sind in JSON nichts als Text.
2.3 Das Dilemma „ein Element oder ein Array“
<list><item>1</item></list>
Bei einem einzelnen item gibt xml2js standardmäßig ein Objekt zurück und erst ab zwei ein Array.
Infolgedessen muss Frontend-Code Array.isArray(x) ? x : [x] schreiben.
Gegenmaßnahme: beim Parsen die Option „immer Array“ einschalten oder das Feld anhand eines Schemas als Arraytyp deklarieren.
import { parseStringPromise } from 'xml2js';
const parsed = await parseStringPromise(xmlText, {
explicitArray: true, // return an array even for a single element
trim: true,
explicitRoot: false,
});
2.4 Sicherheitshinweis
Vor dem Parsen nicht vertrauenswürdiger XML-Daten müssen externe Entities deaktiviert werden, sonst kann XXE ausgelöst werden (Lesen lokaler Dateien, Auskundschaften interner Netze):
// In the Node ecosystem, DTDs and external entities are usually disabled through parser options
const parserOptions = { doctype: false, externalEntities: false };
XML → JSON ist nur die erste Konvertierungsschicht; das Konvertieren beseitigt nicht die Angriffsfläche, die XML mitbringt.
3. Wohin die drei Formate passen
| Szenario | Empfohlenes Format | Grund |
|---|---|---|
| Öffentliche HTTP-APIs | JSON | Nativ von Browsern und jeder Sprache unterstützt |
| Von Hand gepflegte Konfiguration | YAML | Kommentare, Einrückung, Lesbarkeit |
| Dokumente mit starkem Schema und Namensräumen (SOAP, Office Open XML, SVG) | XML | Strukturelle Fähigkeiten und bestehendes Ökosystem |
| Interaktion mit Betriebswerkzeugen (K8s, CI) | YAML | Der Ökosystem-Standard |
| Persistenz, Caches, Logs | JSON / JSONL | Universal und abhängigkeitsfrei |
4. Umsetzungshinweise
import { stringify as toYaml } from 'yaml';
/**
* Converts a JSON value to YAML text while keeping Chinese characters unescaped.
* @param value The JSON value to convert
* @returns The YAML text
*/
function jsonToYaml(value: unknown): string {
return toYaml(value, {
indent: 2,
lineWidth: 0, // disable automatic wrapping so long strings are not split
defaultStringType: 'QUOTE_DOUBLE',
});
}
Einige praxisnahe Optionen:
lineWidth: 0schaltet das automatische Umbinden ab; sonst werden lange URLs über Zeilen getrennt und schwer lesbar;- alles mit doppelten Anführungszeichen zu quotieren verhindert, dass Werte wie
NOund1.10falsch gelesen werden; - bei Daten mit sensibler numerischer Präzision vor dem Serialisieren in Strings umwandeln.
Für die XML-Richtung eine einzige Abbildungsfunktion festlegen und „Attributpräfix“, „Textschlüsselname“ und „Array-Strategie“ in ihre Signatur legen, statt sie überall zu verstreuen:
type XmlMappingOptions = {
attributePrefix: string;
textKey: string;
alwaysArray: boolean;
};
const defaultMapping: XmlMappingOptions = {
attributePrefix: '@',
textKey: '#text',
alwaysArray: false,
};
5. Checkliste
- [ ] Jede „zahl-/booleanähnliche“ Kennung in YAML ist quotiert;
- [ ] Ein Validierungsbeispielset für JSON → YAML → JSON ist definiert und durch Tests abgedeckt;
- [ ] Müssen Kommentare erhalten bleiben, wird die Document-API statt eines Objekt-Roundtrips verwendet;
- [ ] Die XML-Abbildungskonvention ist aufgeschrieben, und das Team teilt einen Parametersatz;
- [ ] DTDs und externe Entities sind beim Parsen nicht vertrauenswürdiger XML-Daten deaktiviert;
- [ ] Byte- und Feldzahlen werden vor und nach der Konvertierung verglichen, jede Anomalie löst Alarm aus.
FAQ
Wie soll ich die Konvertierungsrichtung zwischen JSON, YAML und XML wählen?
Bevorzugen Sie JSON als Zwischenformat: YAML und XML zuerst in ein Datenmodell parsen und dann aus diesem Modell serialisieren, statt Text umzuschreiben. Verwenden Sie die YAML-Document-API, wenn Kommentare überleben müssen, und legen Sie eine XML-Abbildungskonvention fest, bevor Sie Attribute oder Namensräume brauchen.
Warum verliert die Konvertierung von XML nach JSON Attribute und Namensräume?
JSON kennt keine Konzepte von Attributen oder Namensräumen, XML-Attribute, Präfixe und Namensraum-URIs müssen also in gewöhnliche Schlüssel gefaltet werden, üblicherweise mit einem @-Präfix oder einem #text-Knoten. Sobald die Abbildungsregeln auseinanderlaufen, erzeugt dasselbe XML unterschiedliches JSON und kann nicht mehr zurückkonvertiert werden.
Sollte ich JSON, YAML und XML langfristig in beide Richtungen synchron halten?
Nein. Kommentare, Anker, XML-Attribute und Namensräume gehen über Formate verlustig, und eine Zweiweg-Synchronisierung häuft ständig Unterschiede an. Wählen Sie ein Quellformat und behandeln Sie die anderen als einmalige Exporte oder temporäre Ansichten.