YAML
« | 06 Dec 2020 | »XML, JSON, CSV und INI sind seit 20 Jahren die Dateiformate, die ich am häufigsten in Projekten zur Datenspeicherung eingesetzt habe.
Seit etwa 10 Jahren kenne ich ein weiteres, nämlich YAML … aber erst heuer habe ich für dieses Format einen Parser geschrieben …
Das rekursive Akronym YAML
steht für “YAML Ain’t Markup Language”, obwohl
mir die originale “Yet Another Markup Language” besser gefallen hat.
Ähnlich wie in JSON
kann man damit hierarchische Datenstrukturen als
- Ja/Nein Werte (Boolean)
- Zahlen (Float/Integer)
- Text (Strings)
- Listen (Arrays)
- Key/Value Objekte (Dictionaries)
darstellen. Allerdings zeichnet sich YAML als besonders “human-readable” aus, da es nicht voraussetzt, dass die Dateneinträge durch Sonderzeichen wie Hochkomma und Klammern abgegrenzt werden, sondern es nutzt Text-Zeilen und Einrückungen (ähnlich wie Python) als primäres Zuordnungsraster.
Das gleiche Datenstruktur würde in JSON
so aussehen:
1{ 2 "version": "1.2.3", 3 "description": "Sample YAML document", 4 "entry_array": [ 5 { 6 "name": "Entry object 1", 7 "id": 1234, 8 "content": "Hello", 9 }, 10 { 11 "name": "Entry object 2", 12 "id": 5678, 13 "content": "World", 14 } 15 ], 16 "meta_data": 17 { 18 "author": "Me", 19 "created": "2020-12-06", 20 "published": true 21 } 22}
Parser für Strings
Die meisten fertigen Parser haben aus meiner Perspektive zwei Nachteile:
- Sie sind entweder an ein Framework gebunden, das viele Abhängigkeiten und Konventionen mitbringt.
- Oder sie nutzen C
char Strings oder C++
std::string
und sind damit recht ineffizient.
Bei der Einführung von R-Value-References in C++11 wurde gemeldet, dass XML Komponenten um 400% schneller wurden, weil dort jedes Attribut als eigener String angelegt wurde.
Nun seit std::string_view
in C++17 sollte das nochmals dramatisch schneller
geworden sein, wenn dieses neue Feature korrekt benutzt wird.
Im GATE Framework sind string_view
und string
bereits zusammengefasst
und daher verspricht eine eigene Implementierung basierend auf gate_string_t
eine wesentlich höhere Effizienz als wenn man Fremdbibliotheken nutzt und
danach Ende hin- und her konvertieren muss.
YAML zerteilen
Während ich mit JSON
und XML
keine so großen Schwierigkeiten hatte, stellte
sich YAML
als ungut harte Nuss heraus. Das Fehlen von Feldbegrenzern wie
Hochkomma versetzt einen an vielen Stellen in den “Probier-mal” Modus.
Ein "a b": "c d"
wäre eindeutig und würde dem Member “a b” den Wert “c d”
zuordnen. Doch in YAML lautet die Zeile a b : c d
und die Leerzeichen vor
und nach dem Doppelpunkt sind zu entfernen, während sie zwischen den
Buchstaben erhalten bleiben müssen.
Auch sind Texte von Zahlen und Booleans schwerer zu unterscheiden.
Die Zeile - 42
ist ein Arrayeintrag eines Integer während
- 42 is the answer
ein Stringeintrag sein sollte.
Aber am Schlimmsten sind Arrays in Objekten und Objekte in Arrays.
Theoretisch unterscheiden sich Unterelemente von ihren Eltern durch eine
tiefere Einrückung. Doch das führende -
eines Array-Eintrag darf auf der gleichen
Ebene wie das Elternelement sein, wenn dieses ein Objekt ist
Im Beispiel hat der Member entries
als Wert ein Array mit zwei Einträgen,
die aus Objekten bestehen, einmal mit den Membern a
und c
und einmal mit
e
und g
.
Dass das Minus auf der gleichen Einrück-Ebene wie das Elternobjekt liegt,
muss man also speziell behandeln. Umgekehrt muss man auch verstehen, dass
der Member c
zum gleichen Objekt wie a
gehört (genau so wie g
und e
).
Und dann gibt es noch eine Art Inline-Format, wo innerhalb von YAML
quasi
wieder JSON
gesprochen wird (aber auch nur mit optionalen Anführungszeichen).
1entries: [ { a: b, c: d }, { e: f, g: h } ]
Muha! Was für ein Kack!
Fazit
Noch muss ich einige Tests mit meinem YAML
Parser machen. Einige Features wie
Referenzen zwischen Objekten werde ich gar nicht unterstützen, weil der
kleinste gemeinsame Nenner zwischen JSON
und YAML
für mich als Richtwert gilt.
Am Ende sollen beide Datenformate gleichwertig gelesen und in einen Objektbaum
geladen werden können und umgekehrt auch wieder zurückgeschrieben werden
können.
Da gehen zwangsläufig ein paar Features verloren.
Doch ich habe schwer unterschätzt, wie komplex das Parsing eines scheinbar “einfacheren” Dateiformats sein kann. Hier gilt tatsächlich wieder der Grundsatz:
Je leichter etwas für Menschen lesbar ist, um so schwieriger tun sich die Maschinen damit … und umgekehrt ebenso.
Wie auch immer … jetzt kann ich endlich auch bei YAML
klugscheißen,
wenn das Thema angesprochen wird.
Den Parser selbst schreiben ist also immer eine gute Idee.