Immer wieder haben wir in der Programmierung die Herausforderung, strukturierte Daten von Nutzern eingeben zu lassen. Ein wunderbares Format dafür ist YAML.

YAML hat den Vorteil, dass es auch von unversierten Nutzern eingegeben werden kann – ein Vorteil gegenüber XML oder JSON, und auch INI ist nicht unbedingt das einfachste Format, wenn es um eine entspannte Leerzeichenhandhabung geht.

So ist die Eingabe und auch Speicherung von YAML eigentlich trivial. Eine <textarea /> im Browser, ein Textfeld in der Datenbank, und ein YAML-Parser zum Umwandeln der Daten in benutzbare Variablen – fertig ist die Konfigurationsmöglichkeit.

YAML-Parser gibt es eigentlich für so ziemlich jede Sprache, wenn auch zumeist nicht als integralen Bestandteil. So muss man z.B. bei PHP weitere Bibliotheken installieren.

Der einfachste YAML-Fall

Für Konfigurationen reicht zumeist ein einfache Untermenge von YAML aus: Eine Sammlung von key: value-Zuordnungen, sogenannte Block Mappings. Diese erlauben tabellarische Zuordnung von strukturierten, schemalosen Daten. Für meinen Fall gibt es eine weitere Einschränkung: Die Werte dürfen nur einzeilig sein:

en_GB: https://www.example.co.uk/
fr_FR: https://www.example.fr/
de_DE: https://www.example.de/

Um den Aufwand für einen echten YAML-Parser zu vermeiden, habe ich mir für exakt diesen sehr einfachen Anwendungsfall einen maximal-einfachen Parser gebaut, der Block Mappings konvertiert.

…in PHP

Der Konverter erzeugt ein strukturiertes, assoziatives Array:

/**
 * Convert lines like `a: b` into an associative array like `['a' => 'b']`.
 * @param  string $string
 * @return array  
 */
function parseSimpleYaml($string) {
  return preg_match_all('/(?:^|\n)\s*([^#].+?)\s*:\s*([^\n#]+)/', trim($string), $matches)
    ? array_combine($matches[1], $matches[2])
    : [$string]
  ;
}

Damit wird aus dem o.a. YAML Block Mapping ein assoziatives Array:

[
  'en_GB' => 'https://www.example.co.uk/',
  'fr_FR' => 'https://www.example.fr/',
  'de_DE' => 'https://www.example.de/',
]

…in JavaScript

Eine ähnliche Methode kann man in JavaScript anwenden:

const parseSimpleYaml = function(yamlString) {
  let yamlObject = {};
  yamlString
    .split("\n")
    .map((i) => {
      return i.match(/^([^#].+?)\s*:\s*([^\n#]+)/);
    })
    .filter((i) => {
      return i !== null
    })
    .forEach((i) => {
      yamlObject[i[1]] =  i[2];
    })
  ;
  return yamlObject;
}

Damit wird aus dem o.a. YAML Block Mapping ein assoziatives Objekt:

{
  'en_GB': 'https://www.example.co.uk/',
  'fr_FR': 'https://www.example.fr/',
  'de_DE': 'https://www.example.de/',
}

Der selbe Fall im INI-Format

Ehrlich gesagt könnte man für o.a. einfaches Speicherformat auch ein vereinfachtes INI-Format verwenden:

en_GB=https://www.example.co.uk/
fr_FR=https://www.example.fr/
de_DE=https://www.example.de/

Strukturell funktioniert das exakt gleich, nur dass der zum Auslesen verwendete reguläre Ausdruck nun wie folgt aussieht:
/(?:^|\n)\s*([^;].+?)\s*=\s*([^\n;]+)/