In diesem Notebook werden textbasierte Datenformate behandelt. Mit ihnen lassen sich strukturierte Daten speichern und zwischen Programmen, Systemen und Programmiersprachen austauschen.
Zunächst werden die wichtigsten Formate JSON, XML und YAML im Überblick vorgestellt und verglichen. Anschließend liegt der Schwerpunkt auf YAML – mit allen wichtigen Details.
Lernziele. Nach dem Durcharbeiten dieses Notebooks sollte Folgendes gelingen:
- textbasierte Serialisierungsformate erklären und ihren Zweck benennen;
- JSON, XML und YAML lesen, unterscheiden und ihre Einsatzgebiete zuordnen;
- YAML im Detail anwenden (Einrückung, Skalare, Collections, mehrzeilige Strings, Anchors/Aliases, mehrere Dokumente).
Hinweis zu den Code-Beispielen. Zur Veranschaulichung werden die Formate mit kurzen Python-Programmen eingelesen und ausgegeben. Die dafür nötigen Python-Grundlagen sind im Abschnitt Das nötige Python für dieses Notebook zusammengefasst und werden an den Beispielen erklärt. Python-Vorkenntnisse sind nicht erforderlich.
Programme halten Daten während der Ausführung im Arbeitsspeicher als Datenstrukturen vor (etwa als Listen oder als Sammlungen von Schlüssel-Wert-Paaren). Sollen diese Daten gespeichert oder über ein Netzwerk verschickt werden, müssen sie in eine lineare Form gebracht werden – also in eine Folge von Zeichen bzw. Bytes.
Eine solche Datenstruktur ist in Python beispielsweise ein Dictionary (Typname dict) – eine Sammlung von Schlüssel-Wert-Paaren, vergleichbar mit einem Wörterbuch (Stichwort → Eintrag). Was ein dict genau ist, wird weiter unten im Abschnitt Das nötige Python für dieses Notebook erklärt.
Textbasierte Formate verwenden für die serialisierte Darstellung menschenlesbaren Text (in der Regel UTF-8-kodiert) und nicht ein kompaktes, nur maschinenlesbares Binärformat.
docker-compose.yml, Konfigurationen von Werkzeugen oder Anwendungen).| Aspekt | Textformate (JSON, XML, YAML, …) | Binärformate (Protobuf, MessagePack, …) |
|---|---|---|
| Lesbarkeit | für Menschen lesbar und bearbeitbar | nicht direkt lesbar |
| Versionsverwaltung (Diff) | gut vergleichbar | schlecht vergleichbar |
| Größe | größer | kompakter |
| Verarbeitungsgeschwindigkeit | langsamer beim Einlesen | schneller |
| Werkzeuge | jeder Texteditor genügt | spezielle Werkzeuge nötig |
Textformate sind die erste Wahl, wenn Menschen die Daten lesen, schreiben oder versionieren – genau das trifft auf Konfigurationsdateien zu.
Obwohl sich die Syntax der Formate stark unterscheidet, beruhen JSON, XML und YAML auf denselben wenigen Bausteinen:
"Berlin", die Zahl 42, ein Wahrheitswert (true/false, also wahr/falsch) oder null für „kein Wert".stadt → "Berlin". Das Prinzip entspricht einem Wörterbuch: Über das Stichwort wird der zugehörige Eintrag gefunden.[8080, 8443].datenbank
├── host → "db.example.com" (Skalar)
└── ports → [8080, 8443] (Liste von Skalaren)
Sind diese Bausteine einmal vertraut, lässt sich jedes der drei Formate rasch lesen.
Die Code-Beispiele verwenden nur wenige Python-Konzepte. Sie werden hier kurz erklärt; mehr Python ist zum Verständnis nicht erforderlich. Eine Code-Zelle wird ausgeführt, indem sie ausgewählt und Shift+Enter gedrückt wird; das Ergebnis erscheint darunter.
Modul importieren. Mit import json wird eine fertige Sammlung von Funktionen (ein Modul) geladen. Eine darin enthaltene Funktion wird über den Modulnamen angesprochen, etwa json.loads(...) – die Funktion loads aus dem Modul json.
Funktionsaufruf und Rückgabewert. ergebnis = json.loads(text) ruft die Funktion mit der Eingabe text auf. Das berechnete Ergebnis wird der Variablen ergebnis zugewiesen und steht danach zur Verfügung.
Ausgabe. print(x) gibt den Wert x aus.
Zeichenkette (str). Text steht in Anführungszeichen: "hallo". Drei doppelte Anführungszeichen am Anfang und am Ende (""") erlauben mehrzeiligen Text. Das wird genutzt, um JSON-/YAML-Beispiele direkt im Code abzulegen.
Liste (list). Eine geordnete Sammlung in eckigen Klammern: [8080, 8443]. Der Zugriff erfolgt über die Position (den Index, beginnend bei 0): liste[0] ergibt das erste Element.
Dictionary (dict). Eine Sammlung von Schlüssel-Wert-Paaren in geschweiften Klammern: {"host": "db.example.com", "port": 5432}. Der Zugriff erfolgt über den Schlüssel: d["host"]. Ein dict ist das Python-Gegenstück zu den Mappings der Datenformate.
for-Schleife. for element in liste: durchläuft eine Liste und weist der Variablen element nacheinander jeden Eintrag zu; der eingerückte Block darunter wird je Eintrag einmal ausgeführt. Ein dict lässt sich ebenso durchlaufen – am bequemsten mit der Methode d.items(). Sie liefert pro Durchlauf ein Paar aus Schlüssel und Wert, das direkt zwei Schleifenvariablen zugewiesen wird: for schluessel, wert in d.items():. Beispiel:
d = {"host": "db.example.com", "port": 5432}
for schluessel, wert in d.items():
print(schluessel, "=>", wert)
# Ausgabe:
# host => db.example.com
# port => 5432
type(x) nennt den Datentyp eines Werts (etwa str oder int); repr(x) liefert eine eindeutige Textdarstellung (bei Zeichenketten zum Beispiel mit Anführungszeichen).
Diese Bausteine genügen, um alle Beispiele zu verstehen.
Aus JavaScript hervorgegangen und heute das Standardformat für Web-Schnittstellen (APIs). JSON ist kompakt, einfach und wird von praktisch jeder Programmiersprache unterstützt.
Ein älteres, sehr mächtiges Format aus der Dokumentenwelt. Es verwendet Tags zur Auszeichnung – das sind Markierungen in spitzen Klammern, etwa <name>…</name>, ähnlich wie in HTML (eine genauere Erklärung folgt im XML-Abschnitt). XML ist im Unternehmensumfeld verbreitet und überall dort, wo Text und Struktur gemischt vorkommen (zum Beispiel in Office-Dokumenten, in SVG-Grafiken oder in RSS-Nachrichtenfeeds).
Auf Lesbarkeit für Menschen optimiert. YAML verzichtet weitgehend auf Klammern und nutzt stattdessen Einrückung. Es ist für Konfigurationsdateien sehr verbreitet (Docker Compose, Kubernetes, CI/CD-Pipelines, Ansible).
[server]) gruppiert; in der Python-Welt zur Projektkonfiguration verbreitet.| Kriterium | JSON | XML | YAML |
|---|---|---|---|
| Lesbarkeit | mittel | gering | hoch |
| Kommentare | ❌ nein | ✅ ja <!-- --> |
✅ ja # |
| Datentypen | Zeichenkette, Zahl, Wahrheitswert, null, Liste, Objekt | grundsätzlich Text (Typen über Schema) | Zeichenkette, Ganzzahl, Gleitkommazahl, Wahrheitswert, null, Listen, Mappings, Datum |
| Syntax | {} [] , : |
<tag>…</tag> |
Einrückung |
| Schema/Validierung | JSON Schema | DTD, XSD | extern (z. B. JSON Schema) |
| Mehrzeilige Zeichenketten | umständlich (\n) |
möglich | sehr komfortabel |
| Referenzen/Wiederverwendung | ❌ nein | begrenzt | ✅ Anchors/Aliases |
| Typischer Einsatz | Web-APIs | Dokumente, Unternehmensumfeld | Konfiguration, DevOps |
Eine wichtige Beziehung: JSON ist eine Teilmenge von YAML – jedes gültige JSON-Dokument ist zugleich gültiges YAML. Darauf wird später eingegangen.
JSON kennt genau zwei Strukturtypen und vier Skalartypen:
{ }: eine ungeordnete Menge von Paaren der Form "schlüssel": wert (Schlüssel sind stets Zeichenketten in "…").[ ]: eine geordnete Liste von Werten."text"), Zahl (42, 3.14), Wahrheitswert (true / false), null (kein Wert).Werte werden durch Kommata getrennt. Wichtig: kein Komma nach dem letzten Element (kein trailing comma), und keine Kommentare.
Beispiel – die Konfiguration eines (fiktiven) Webservices:
{
"name": "webservice",
"version": 2,
"enabled": true,
"replicas": 3,
"ports": [8080, 8443],
"database": {
"host": "db.example.com",
"port": 5432
},
"tags": null
}
Erläuterung des Beispiels. Das äußere { … } ist ein Objekt mit mehreren Feldern. name ist eine Zeichenkette; version und replicas sind Zahlen; enabled ist ein Wahrheitswert. ports ist ein Array (eine Liste) mit zwei Zahlen. Das Feld database ist selbst wieder ein Objekt – ein Beispiel für Verschachtelung – mit den Feldern host und port. Das Feld tags hat den Wert null, ist also bewusst leer gelassen.
Python enthält ein fertiges Modul namens json, das zwischen JSON-Text und Python-Datenstrukturen umwandelt:
json.loads(text) → Python-Datenstruktur (loads = load from string) — entspricht der Deserialisierung.json.dumps(obj) → JSON-Text (dumps = dump to string) — entspricht der Serialisierung.Die Typen entsprechen einander wie folgt: JSON-Objekt ↔ dict, JSON-Array ↔ list, true/false ↔ True/False, null ↔ None.
import json # Modul aus der Python-Standardbibliothek laden
# Dreifache Anfuehrungszeichen erlauben mehrzeiligen Text:
json_text = """
{
"name": "webservice",
"version": 2,
"enabled": true,
"ports": [8080, 8443],
"database": {"host": "db.example.com", "port": 5432},
"tags": null
}
"""
# Deserialisierung: JSON-Text -> Python-Datenstruktur (hier ein dict)
data = json.loads(json_text)
print(type(data)) # <class 'dict'>
print(data["database"]["port"]) # Zugriff ueber Schluessel: 5432
print(data["enabled"], data["tags"]) # True None (aus true / null)
# Serialisierung: Python-Datenstruktur -> JSON-Text
obj = {
"name": "webservice",
"ports": [8080, 8443], # eine Liste
"enabled": True,
"tags": None,
}
# indent=2 sorgt fuer eine eingerueckte, gut lesbare Ausgabe
print(json.dumps(obj, indent=2))
Mit JSON Schema wird – selbst wieder in JSON formuliert – beschrieben, welche Felder vorhanden sein müssen und welche Typen sie haben. Werkzeuge können eine Datei dann automatisch dagegen prüfen (validieren). Dies wird hier nur zur Einordnung erwähnt.
XML beschreibt Daten mit Elementen, die durch Tags begrenzt werden. Ein Tag ist eine Markierung in spitzen Klammern. Zu jedem Element gehören ein Start-Tag <name> und ein End-Tag </name>. Ein Element kann
<server port="8080"> (das Attribut port hat den Wert 8080).Es gibt genau ein Wurzelelement (root), das alle übrigen umschließt.
Dasselbe Beispiel wie zuvor, nun in XML:
<?xml version="1.0" encoding="UTF-8"?>
<webservice version="2" enabled="true">
<ports>
<port>8080</port>
<port>8443</port>
</ports>
<database host="db.example.com" port="5432"/>
<!-- Kommentare sind in XML erlaubt -->
</webservice>
Auffällig ist, dass XML deutlich ausführlicher als JSON oder YAML ist, weil jeder Tag-Name doppelt erscheint (im Start- und im End-Tag). Außerdem besteht für denselben Wert oft die Wahl zwischen einem Attribut und einem Kind-Element.
Die Python-Standardbibliothek stellt dafür das Modul xml.etree.ElementTree bereit (hier mit ET abgekürzt). Es liest XML-Text ein und stellt ihn als Baum von Elementen dar, durch den navigiert werden kann.
import xml.etree.ElementTree as ET # Modul laden und kurz "ET" nennen
xml_text = """
<webservice version="2" enabled="true">
<ports>
<port>8080</port>
<port>8443</port>
</ports>
<database host="db.example.com" port="5432"/>
</webservice>
"""
root = ET.fromstring(xml_text) # XML-Text einlesen -> Wurzelelement
print(root.tag) # Name des Tags: webservice
print(root.attrib) # Attribute als dict: {'version': '2', 'enabled': 'true'}
# Alle <port>-Elemente durchlaufen und ihren Textinhalt einsammeln.
# Hinweis: Text aus XML ist immer eine Zeichenkette ('8080', nicht 8080).
ports = []
for p in root.findall("./ports/port"):
ports.append(p.text) # .text ist der Inhalt zwischen <port> und </port>
print(ports) # ['8080', '8443']
db = root.find("database")
print(db.attrib["host"], db.attrib["port"]) # db.example.com 5432
Rund um XML existiert eine ganze Familie verwandter Standards:
xmlns:…).YAML steht (selbstbezüglich-ironisch) für „YAML Ain't Markup Language". Das Entwurfsziel ist eine möglichst gute Lesbarkeit für Menschen. YAML verzichtet weitgehend auf Klammern und Anführungszeichen und nutzt stattdessen Einrückung zur Strukturierung – ähnlich wie es Python beim Quelltext tut.
Zwei zentrale Punkte vorab:
In den folgenden Beispielen wird die Python-Bibliothek PyYAML verwendet (Import mit import yaml). Falls sie nicht installiert ist, kann sie auf der Kommandozeile mit pip install pyyaml nachinstalliert werden. Die zentrale Funktion ist yaml.safe_load(text), die einen YAML-Text einliest und – wie json.loads – eine Python-Datenstruktur (meist ein dict oder eine list) zurückgibt.
# Falls noetig zuvor in der Shell installieren: pip install pyyaml
import yaml
print("PyYAML-Version:", yaml.__version__)
Die Einrückung legt fest, was zu was gehört (die Verschachtelung). Es gelten folgende Regeln:
name: webservice
database:
host: db.example.com # zwei Leerzeichen eingerueckt -> gehoert zu 'database'
port: 5432
Das entspricht dem JSON {"name": "webservice", "database": {"host": "db.example.com", "port": 5432}}.
YAML errät den Typ eines nicht in Anführungszeichen gesetzten Skalars:
eine_zeichenkette: hallo welt # Zeichenkette (ganz ohne Anfuehrungszeichen!)
eine_ganzzahl: 42 # Ganzzahl (int)
eine_kommazahl: 3.14 # Gleitkommazahl (float)
ein_wahrheitswert: true # Wahrheitswert (bool)
nichts: null # kein Wert (in Python: None; auch ~ oder leer)
ein_datum: 2026-05-29 # Datum
Zeichenketten benötigen in der Regel keine Anführungszeichen. Setzen lassen sie sich dennoch:
'einfach' (einfache Anführungszeichen): nahezu keine Sonderzeichen-Auflösung."doppelt" (doppelte Anführungszeichen): Escape-Sequenzen wie \n (Zeilenumbruch) oder \t (Tabulator) werden interpretiert.import yaml
doc = """
eine_zeichenkette: hallo welt
eine_ganzzahl: 42
eine_kommazahl: 3.14
ein_wahrheitswert: true
nichts: null
ein_datum: 2026-05-29
"""
data = yaml.safe_load(doc) # YAML-Text -> Python-dict
# data.items() liefert die (Schluessel, Wert)-Paare; type(v) nennt den Typ:
for schluessel, wert in data.items():
print(schluessel, "->", repr(wert), " Typ:", type(wert).__name__)
1. Wahrheitswerte (das „Norway-Problem"). YAML (in der weit verbreiteten Version 1.1) deutet zahlreiche Wörter als Wahrheitswert: yes/no, on/off, true/false. Dadurch wird auch das Länderkürzel NO (Norwegen) zu False!
laender:
- NO # wird zu False statt zur Zeichenkette "NO"!
- SE
2. Führende Null und Versionsnummern. version: 1.10 wird zur Gleitkommazahl 1.1. Ein Wert mit führender Null wird als Oktalzahl gelesen: 01067 ergibt 567! (Das gilt nur, wenn alle Ziffern zwischen 0 und 7 liegen; 08000 bleibt zufällig eine Zeichenkette – also inkonsistent und unzuverlässig.) Betroffen sind etwa Postleitzahlen oder Telefonnummern.
Abhilfe: im Zweifel in Anführungszeichen setzen – dann ist der Wert garantiert eine Zeichenkette:
laender: ["NO", "SE"]
version: "1.10"
plz: "01067" # sonst Oktalzahl 567
import yaml
tricky = """
ohne_anfuehrungszeichen:
- NO
- yes
- on
- 1.10
- 01067
mit_anfuehrungszeichen:
- "NO"
- "1.10"
- "01067"
"""
print(yaml.safe_load(tricky))
# NO/yes/on werden zu Wahrheitswerten, 1.10 zur Kommazahl, 01067 zur Oktalzahl 567.
Mapping (Schlüssel-Wert-Paare; entspricht einem Python-dict):
database:
host: db.example.com
port: 5432
Begriffsklärung „Mapping". Der Begriff Mapping (deutsch Abbildung) bezeichnet die gesamte Zuordnung über alle Schlüssel – nicht ein einzelnes Schlüssel-Wert-Paar. Das obige Mapping bildet zwei Schlüssel ab (
hostundport) und besteht daher aus zwei Einträgen (Paaren), ist aber ein Mapping. Ein einzelnes Paar wiehost: db.example.comist nur ein Eintrag dieser Abbildung. (Analogie aus der Mathematik: Die Funktion $f(x)=x^2$ ist eine Abbildung, obwohl ihr „Graph" aus vielen Paaren besteht – $(1,1), (2,4), (3,9), \dots$. Die Abbildung ist die ganze Zuordnungsvorschrift, nicht ein einzelnes Paar.)
Sequenz (Liste; entspricht einer Python-list) – jedes Element beginnt mit - (Bindestrich und Leerzeichen):
ports:
- 8080
- 8443
Zusätzlich gibt es den Flow-Stil (JSON-ähnlich, in einer Zeile):
ports: [8080, 8443]
database: {host: db.example.com, port: 5432}
Der Block-Stil (eingerückt, mehrzeilig) dient der Lesbarkeit; der Flow-Stil eignet sich für kurze, kompakte Werte.
import yaml
doc = """
# Block-Stil
ports:
- 8080
- 8443
database:
host: db.example.com
port: 5432
# Flow-Stil (gleichbedeutend)
ports_flow: [8080, 8443]
db_flow: {host: db.example.com, port: 5432}
"""
print(yaml.safe_load(doc))
Mappings und Sequenzen lassen sich beliebig kombinieren: Der Wert eines Schlüssels darf selbst wieder ein Mapping oder eine Sequenz sein. So entsteht die baumartige Struktur.
Mapping innerhalb eines Mappings ("Mapping von Mappings"). Im Beispiel
database:
host: db.example.com
port: 5432
ist database ein Schlüssel, dessen Wert selbst wieder ein Mapping ist – nämlich das eine Mapping {host: db.example.com, port: 5432}. Wichtig: Das ist ein Mapping mit zwei Einträgen (zwei Schlüssel-Wert-Paaren), nicht etwa zwei Mappings. Die Zeile host: db.example.com für sich ist kein eigenes Mapping, sondern nur ein Eintrag dieses Mappings. Die Gesamtstruktur ist also ein Mapping, dessen Wert erneut ein Mapping ist (dessen Werte hier Skalare sind). In Python entspricht das einem dict, das ein weiteres dict mit zwei Einträgen enthält: {"database": {"host": "db.example.com", "port": 5432}}.
Liste von Mappings. Ebenso häufig ist eine Liste, deren Elemente Mappings sind – etwa mehrere Benutzer mit jeweils mehreren Feldern:
users:
- name: alice
role: admin
- name: bob
role: user
Auf die Einrückung ist zu achten: Das - kennzeichnet ein Listenelement; name und role desselben Elements stehen auf gleicher Tiefe.
import yaml
doc = """
users:
- name: alice
role: admin
active: true
- name: bob
role: user
active: false
"""
data = yaml.safe_load(doc)
print(type(data["users"]).__name__, "mit", len(data["users"]), "Eintraegen")
print(data["users"][0]) # erstes Element: {'name': 'alice', 'role': 'admin', 'active': True}
Für längere Texte (etwa Skripte oder Befehle) gibt es zwei Block-Stile:
|: Zeilenumbrüche bleiben erhalten.>: Zeilenumbrüche werden zu Leerzeichen zusammengefaltet (eine Leerzeile erzeugt dennoch einen echten Umbruch).literal: |
Zeile 1
Zeile 2
folded: >
Diese Zeilen werden
zu einer langen Zeile
zusammengefuegt.
Chomping-Indikatoren steuern den abschließenden Zeilenumbruch:
| / > : clip – genau ein abschließender Umbruch (Standardverhalten).|- / >- : strip – kein abschließender Umbruch.|+ / >+ : keep – alle abschließenden Leerzeilen bleiben erhalten.import yaml
doc = """
literal: |
Zeile 1
Zeile 2
literal_strip: |-
ohne abschliessenden Umbruch
folded: >
Diese Zeilen werden
zu einer langen Zeile
zusammengefuegt.
"""
data = yaml.safe_load(doc)
for schluessel, wert in data.items():
print("---", schluessel, "---")
print(repr(wert)) # repr macht die Zeilenumbrueche (\n) sichtbar
Alles ab einem # bis zum Zeilenende ist ein Kommentar und wird beim Einlesen ignoriert. Das ist einer der größten Vorteile gegenüber JSON für Konfigurationsdateien.
# Konfiguration des Webservices
port: 8080 # Standard-HTTP-Port (intern)
YAML kann Teile eines Dokuments benennen und wiederverwenden; das vermeidet Duplikate:
&name: markiert einen Knoten mit einem Namen.*name: verweist auf den markierten Knoten und fügt ihn ein.<<: fügt die Schlüssel eines so markierten Mappings ein (und erlaubt, einzelne Werte zu überschreiben).defaults: &defaults # Anchor mit dem Namen 'defaults'
restart: always
logging: json
service_a:
<<: *defaults # uebernimmt restart und logging
image: nginx
service_b:
<<: *defaults
image: redis
restart: on-failure # ueberschreibt den geerbten Wert
import yaml
doc = """
defaults: &defaults
restart: always
logging: json
service_a:
<<: *defaults
image: nginx
service_b:
<<: *defaults
image: redis
restart: on-failure
"""
data = yaml.safe_load(doc)
print("service_a:", data["service_a"])
print("service_b:", data["service_b"]) # restart wurde ueberschrieben
Mit Tags lässt sich der Typ erzwingen, falls die automatische Erkennung nicht ausreicht:
als_zeichenkette: !!str 42 # ergibt "42" statt der Zahl 42
als_ganzzahl: !!int "42" # ergibt 42 statt der Zeichenkette "42"
Es gibt auch anwendungsspezifische Tags. Im Alltag werden Tags selten benötigt – meist genügt das Setzen von Anführungszeichen. Sicherheitshinweis: Manche Tags können beim Einlesen Objekte erzeugen. Deshalb sollte stets safe_load verwendet werden (siehe Abschnitt YAML mit Python: PyYAML im Detail).
Eine YAML-Datei kann mehrere Dokumente enthalten, getrennt durch ---. Optional markiert ... das Ende eines Dokuments. Dies wird zum Beispiel von Kubernetes intensiv genutzt (mehrere Ressourcen in einer Datei).
---
name: dokument_1
---
name: dokument_2
...
Warum mehrere Dokumente? Manchmal gehören mehrere voneinander unabhängige Konfigurationen logisch zusammen und sollen in einer Datei abgelegt werden – etwa bei Kubernetes mehrere Ressourcen (ein Service, ein Deployment usw.). Statt vieler Einzeldateien genügt eine Datei, deren Dokumente durch --- getrennt sind.
Einlesen – safe_load gegenüber safe_load_all:
yaml.safe_load(text) erwartet genau ein Dokument. Enthält der Text mehrere (durch --- getrennte) Dokumente, führt das zu einem Fehler.yaml.safe_load_all(text) ist für mehrere Dokumente gedacht. Es liest sie nicht alle auf einmal ein, sondern liefert einen Iterator: ein Objekt, das die Dokumente nacheinander bereitstellt – pro ----Abschnitt ein Dokument. Jedes gelieferte Dokument ist eine eigene Python-Datenstruktur (meist ein dict).Üblicherweise wird dieser Iterator mit einer for-Schleife durchlaufen, die jedes Dokument einzeln verarbeitet. Werden alle Dokumente zugleich als Liste benötigt, lässt sich der Iterator mit list(...) einsammeln: dokumente = list(yaml.safe_load_all(text)); danach ist etwa dokumente[0] das erste Dokument.
Im folgenden Beispiel ergänzt enumerate(...) zusätzlich einen Zähler, sodass neben dem Dokument auch dessen laufende Nummer (beginnend bei 0) ausgegeben wird.
import yaml
doc = """
---
name: dokument_1
typ: config
---
name: dokument_2
typ: secret
"""
# safe_load_all liefert die Dokumente nacheinander; enumerate ergaenzt einen Zaehler:
for nummer, dokument in enumerate(yaml.safe_load_all(doc)):
print(nummer, dokument)
Da JSON eine Teilmenge von YAML ist, kann der YAML-Parser JSON unmittelbar einlesen. Das ist für Konvertierungen praktisch: JSON hinein, YAML heraus (und umgekehrt).
import yaml
# JSON ist gueltiges YAML und laesst sich daher direkt mit safe_load einlesen:
json_text = '{"name": "webservice", "ports": [8080, 8443], "enabled": true}'
data = yaml.safe_load(json_text)
print("aus JSON eingelesen:", data)
# und wieder als (gut lesbares) YAML ausgeben:
print("--- als YAML ---")
print(yaml.dump(data, sort_keys=False, default_flow_style=False))
yaml.safe_load(text) → Python-Datenstruktur (sicher, empfohlen).yaml.safe_load_all(text) → liefert nacheinander mehrere Dokumente.yaml.safe_dump(obj) bzw. yaml.dump(obj) → erzeugt YAML-Text.Sicherheit – wichtig: Bei Daten aus nicht vertrauenswürdiger Quelle ist stets safe_load zu verwenden, niemals das ältere yaml.load(text) ohne Angabe eines Loader. Das vollständige load kann über Tags beliebige Python-Objekte erzeugen (bis hin zur Ausführung von Code). safe_load lässt nur die Standard-Datentypen zu.
Nützliche Optionen von dump:
sort_keys=False – behält die Reihenfolge der Schlüssel bei.default_flow_style=False – erzwingt den (lesbareren) Block-Stil.allow_unicode=True – gibt Umlaute und andere Unicode-Zeichen direkt aus, statt sie zu maskieren.Soll beim Bearbeiten der Kommentar erhalten bleiben (ein sogenannter Round-Trip), ist die Bibliothek ruamel.yaml besser geeignet – PyYAML verwirft Kommentare beim Einlesen.
import yaml
obj = {
"name": "wörterbuch",
"ports": [8080, 8443],
"database": {"host": "db.example.com", "port": 5432},
}
# Standardausgabe: schlechter lesbar (Schluessel sortiert, Unicode maskiert)
print(yaml.dump(obj))
print("=== besser lesbar ===")
print(yaml.dump(obj, sort_keys=False, default_flow_style=False, allow_unicode=True))
NO, yes, 1.10, 01067 in Anführungszeichen setzen, wenn eine Zeichenkette gemeint ist.url: http://x:8080) kann zu Verwirrung führen → in Anführungszeichen setzen.ruamel.yaml verwenden.safe_load einsetzen.Zum direkten Vergleich derselbe Inhalt in allen drei Formaten.
JSON
{
"name": "webservice",
"enabled": true,
"ports": [8080, 8443],
"database": {"host": "db.example.com", "port": 5432}
}
XML
<webservice enabled="true">
<name>webservice</name>
<ports>
<port>8080</port>
<port>8443</port>
</ports>
<database host="db.example.com" port="5432"/>
</webservice>
YAML
name: webservice
enabled: true
ports:
- 8080
- 8443
database:
host: db.example.com
port: 5432
Gut erkennbar: YAML ist am kompaktesten und am besten lesbar, XML am ausführlichsten.
import json, yaml
# Ausgangspunkt ist eine Python-Datenstruktur; gezeigt werden JSON und YAML:
obj = {
"name": "webservice",
"enabled": True,
"ports": [8080, 8443],
"database": {"host": "db.example.com", "port": 5432},
}
print("===== JSON =====")
print(json.dumps(obj, indent=2))
print("\n===== YAML =====")
print(yaml.dump(obj, sort_keys=False, default_flow_style=False))
Die Aufgaben sind in den darunterliegenden Code-Zellen zu bearbeiten. Hilfreich sind yaml.safe_load und json.loads.
Die folgenden drei Snippets beschreiben jeweils denselben Inhalt, aber in unterschiedlichen Formaten. Für jedes Snippet (A, B, C) ist anzugeben, ob es sich um JSON, XML oder YAML handelt, und die Entscheidung ist kurz zu begründen (woran ist das Format erkennbar?).
Snippet A
name: webservice
ports:
- 8080
- 8443
Snippet B
<webservice>
<name>webservice</name>
<ports>
<port>8080</port>
<port>8443</port>
</ports>
</webservice>
Snippet C
{"name": "webservice", "ports": [8080, 8443]}
Das folgende YAML ist einzulesen. Auszugeben sind die Liste aller Service-Namen sowie die Ports von web.
import yaml
aufgabe2 = """
services:
web:
image: nginx:1.27
ports:
- "8080:80"
db:
image: postgres:16
"""
# Aufgabe: einlesen, danach Service-Namen und die Ports von web ausgeben
Das folgende YAML enthält ein Problem durch die automatische Typerkennung. Es ist einzulesen, der fehlinterpretierte Wert ist zu finden, und das YAML ist so zu korrigieren, dass alle Werte Zeichenketten bleiben.
import yaml
aufgabe3 = """
laender:
- NO
- SE
- DK
plz: 01067
version: 1.10
"""
# Aufgabe: einlesen, Problem(e) erkennen, korrigierte Variante mit Zeichenketten erstellen
print(yaml.safe_load(aufgabe3))
Zu erstellen ist eine compose-ähnliche Struktur mit zwei Services, die sich eine gemeinsame Konfiguration (restart: always, logging: json) über einen Anchor und einen Merge Key teilen. Ein Service soll restart überschreiben. Das Ergebnis ist mit safe_load einzulesen und zu prüfen.
import yaml
# Aufgabe: YAML-Text mit &anchor und <<: *anchor erstellen und einlesen
aufgabe4 = """
"""
# print(yaml.safe_load(aufgabe4))
docker-compose.yml korrigieren¶Die folgende Datei lässt sich nicht einlesen bzw. wird falsch interpretiert. Es sind alle Fehler zu finden (Einrückung, Verwechslung von Liste und Mapping, fehlende Anführungszeichen) und eine korrekte Fassung ist zu erstellen. Hinweis: yaml.safe_load löst bei Syntaxfehlern eine Ausnahme (Exception) aus; diese lässt sich abfangen und ihre Meldung lesen.
import yaml
kaputt = """
services:
- web:
image: nginx
ports:
- 8080:80
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: geheim
"""
try:
print(yaml.safe_load(kaputt))
except yaml.YAMLError as e:
print("YAML-Fehler:", e)
# Aufgabe: korrigierte Fassung in 'korrekt' erstellen und erfolgreich einlesen
korrekt = """
"""
Das folgende JSON ist einzulesen und anschließend als gut lesbares YAML auszugeben (sort_keys=False, Block-Stil, allow_unicode=True). Danach ist es wieder nach JSON zurückzuwandeln.
import json, yaml
aufgabe6 = '{"name": "wörterbuch", "ports": [8080, 8443], "db": {"host": "x", "port": 5432}}'
# Aufgabe: JSON -> Python -> als YAML ausgeben -> wieder nach JSON
Wann welches Format?
Wichtige YAML-Punkte
schlüssel: wert) und Sequenzen (- element); Block- gegenüber Flow-Stil.NO, yes, 1.10, Port-Zuordnungen) → im Zweifel in Anführungszeichen setzen.| (literal) und > (folded).&anchor, *alias, << (Merge Key); in compose oft über x--Extension-Fields.---.safe_load verwenden; zum Erhalt von Kommentaren ruamel.yaml.Spezifikationen und Verweise