std.xml

XML erzeugen und einfache Dokumente zerlegen: Sonderzeichen maskieren, Deklaration, Elemente und Dokumente schreiben, ein Element in eine binäre Eintragsstruktur überführen, formatieren. Die Unit hat kein Dokumentmodell und keinen Baum — sie arbeitet zeichenweise über den Text.

→ std.html · std.string · Standard Library

Alle Beispiele dieser Seite sind mit lyxc 1.0.21A übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben.

Einsatz: Ausgabe von Konfigurationen, RSS/Atom-Feeds, sitemap.xml, einfacher Datenaustausch. Für das Lesen fremder Dokumente ist die Unit nur begrenzt geeignet — siehe die Kästen unten.

 
Die vier Mängel dieser Unit sind behoben — nachgemessen mit lyxc 1.1.11B gegen die ausgelieferte std/xml.lyx:

* IsValid prüft Wohlgeformtheit: <a wird abgewiesen, <a></a> gilt als gültig (#1422).
* ParseString schreibt in den Ausgabepuffer und meldet 0, wenn kein Wurzelelement da ist (#1423) — Format siehe unten.
* PrettyPrint rührt Textinhalte nicht mehr an (#1424).
* Die drei Write…-Funktionen terminieren ihre Ausgabe (#1425).

ParseString: das Ergebnisformat

ParseString(input, output) füllt einen festen Satz von Feldern im Ausgabepuffer, keine Zeichenkette. Der Puffer muss XML_PARSE_BUFFER_SIZE (576 Byte) groß sein; die Feldanfänge sind exportierte Konstanten:

Konstante Versatz Feld Größe
XML_OFF_VERSION 0 Version aus der Deklaration 32
XML_OFF_ENCODING 32 Kodierung aus der Deklaration 32
XML_OFF_ROOTNAME 64 Name des Wurzelelements 256
XML_OFF_ROOTTEXT 320 Textinhalt des Wurzelelements 256

import std.io;
import std.xml;
import std.alloc;

fn main(): int64 {
  var o: pchar := alloc(XML_PARSE_BUFFER_SIZE) as pchar;
  var rc: int64 := ParseString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><gruss>hallo</gruss>"c, o);

  Print("rc="c); Print(rc); PrintLn(""c);
  Print("Version : "c); PrintLn((o as int64 + XML_OFF_VERSION)  as pchar);
  Print("Encoding: "c); PrintLn((o as int64 + XML_OFF_ENCODING) as pchar);
  Print("Wurzel  : "c); PrintLn((o as int64 + XML_OFF_ROOTNAME) as pchar);
  Print("Text    : "c); PrintLn((o as int64 + XML_OFF_ROOTTEXT) as pchar);
  return 0;
}

rc=1
Version : 1.0
Encoding: UTF-8
Wurzel  : gruss
Text    : hallo

Ohne Wurzelelement — etwa bei „kein xml“ — liefert ParseString 0, und ROOTNAME wie ROOTTEXT sind leer. Der Rückgabewert ist damit brauchbar; die frühere Ausgabe „immer 1„ gibt es nicht mehr.


Maskieren

Signatur Maskiert Ergänzt Anführungszeichen
EscapeText(input: pchar, output: pchar): int64 &, <, > nein
EscapeAttribute(input: pchar, output: pchar): int64 &, <, “ ja — der Wert wird gleich in „…“ gesetzt

Die Aufteilung folgt der XML-Spezifikation: Im Textinhalt ist „ unkritisch, in einem Attributwert dagegen das Anführungszeichen — und > ist dort erlaubt. Beide Funktionen hängen ein Nullbyte an und liefern die Länge ohne dieses.

 
EscapeAttribute schreibt die Anführungszeichen selbst. Wer sie im eigenen Text zusätzlich setzt, bekommt doppelte. Für die Ausgabe also name= + Ergebnis, ohne weitere Zeichen.

Anders als std.html fasst die Unit kein Byte 160 an — UTF-8-Text bleibt unversehrt.

Schreiben

Signatur Beschreibung
WriteDeclaration(output, version, encoding): int64 <?xml version=„1.0“ encoding=„UTF-8“?>
WriteElement(output, tagName, attrs, attrCount, text, indent): int64 Ein Element mit Attributen, Text und Einzug; ohne Text entsteht &lt;tag/&gt;. Schließt mit Zeilenumbruch.
WriteDocument(output, rootName, rootText, version, encoding): int64 Deklaration + Wurzelelement in einem Aufruf

Attribut-Format: attrs ist ein zusammenhängender Speicherbereich mit 128 Byte je Attribut — die ersten 64 Byte der Name, die zweiten 64 der Wert, jeweils nullterminiert. Für attrCount Attribute also attrCount × 128 Byte. Werte werden nicht maskiert; wer Sonderzeichen erwartet, ruft vorher EscapeText (ohne die Anführungszeichen von EscapeAttribute).

 
Die drei Write…-Funktionen terminieren ihre Ausgabe (#1425, nachgemessen mit lyxc 1.1.11B in einem vorher mit X gefüllten Puffer): nach dem geschriebenen Text steht ein Nullbyte, Reste eines früheren Inhalts scheinen nicht mehr durch. Der Rückgabewert ist weiterhin die Länge. Das StrSetChar(o, n, 0); in älteren Beispielen ist damit überflüssig, schadet aber nicht.

import std.xml;
import std.alloc;
import std.string;

fn main(): int64 {
    var o: pchar := alloc(1024) as pchar;

    // Escapen: Text und Attributwert unterscheiden sich
    EscapeText("Tom & Jerry <b> \"zitiert\""c, o);
    PrintLn(StrConcat("EscapeText:      ", o));
    EscapeAttribute("Tom & Jerry <b> \"zitiert\""c, o);
    PrintLn(StrConcat("EscapeAttribute: ", o));

    // Dokument schreiben - Write... terminiert NICHT, also selbst nullsetzen
    var n: int64 := WriteDocument(o, "config"c, "Inhalt"c, "1.0"c, "UTF-8"c);
    StrSetChar(o, n, 0);
    PrintLn("WriteDocument:");
    PrintLn(o);

    // Element mit zwei Attributen:
    // Namen liegen bei Offset j*128, Werte bei j*128+64 - je hoechstens 64 Byte
    var attrs: pchar := alloc(256) as pchar;
    var i: int64 := 0;
    while (i < 256) { StrSetChar(attrs, i, 0); i := i + 1; }
    StrSetChar(attrs, 0, 105); StrSetChar(attrs, 1, 100);     // "id"
    StrSetChar(attrs, 64, 55);                                 // "7"
    StrSetChar(attrs, 128, 116); StrSetChar(attrs, 129, 121);  // "ty"
    StrSetChar(attrs, 192, 65);                                // "A"

    n := WriteElement(o, "item"c, attrs, 2, "Text"c, 2);
    StrSetChar(o, n, 0);
    Print(StrConcat("WriteElement:    ", o));

    n := WriteElement(o, "leer"c, 0 as pchar, 0, ""c, 0);
    StrSetChar(o, n, 0);
    Print(StrConcat("leeres Element:  ", o));
    return 0;
}

EscapeText:      Tom &amp; Jerry &lt;b&gt; "zitiert"
EscapeAttribute: "Tom &amp; Jerry &lt;b> &quot;zitiert&quot;"
WriteDocument:
<?xml version="1.0" encoding="UTF-8"?>
<config>Inhalt</config>

WriteElement:      <item id="7" ty="A">Text</item>
leeres Element:  <leer/>


Die Array-Familie

Der einzige Weg dieser Unit, XML-Inhalt tatsächlich auszulesen. Ein Eintrag ist ein binärer Block:

Offset Inhalt
0–1 Länge des Namens (zwei Byte, niederwertiges zuerst)
2 … Name
danach 2 Byte Länge des Textes
danach Text
Signatur Beschreibung
XMLToArray(xml: pchar, output: pchar): int64 Wurzelelement in diese Form überführen; 1 bei Erfolg, 0 wenn kein < gefunden wird
CreateArrayEntry(name: pchar, text: pchar, output: pchar): int64 Eintrag von Hand aufbauen; liefert die Bytezahl
GetArrayEntryName(arr: pchar, output: pchar): int64 Namen herauslesen (nullterminiert)
GetArrayEntryText(arr: pchar, output: pchar): int64 Text herauslesen (nullterminiert)
ArrayToXML(arr: pchar, output: pchar): int64 Zurück nach &lt;name&gt;text&lt;/name&gt;

import std.xml;
import std.alloc;
import std.string;

fn J(b: bool): pchar { if (b) { return "gueltig"; } return "ungueltig"; }
fn Leer(p: pchar, n: int64): void { var i: int64 := 0; while (i < n) { StrSetChar(p, i, 0); i := i + 1; } }

fn main(): int64 {
    var arr: pchar := alloc(1024) as pchar;
    var o:   pchar := alloc(1024) as pchar;
    Leer(arr, 1024); Leer(o, 1024);

    // Eintrag anlegen: 2 Byte Namenslaenge, Name, 2 Byte Textlaenge, Text
    var n: int64 := CreateArrayEntry("titel"c, "Wert"c, arr);
    PrintLn(StrConcat("CreateArrayEntry, Bytes: ", IntToStr(n)));
    GetArrayEntryName(arr, o); PrintLn(StrConcat("  Name: ", o));
    Leer(o, 1024);
    GetArrayEntryText(arr, o); PrintLn(StrConcat("  Text: ", o));

    // Und zurueck nach XML
    Leer(o, 1024);
    PrintLn(StrConcat("ArrayToXML, Bytes: ", IntToStr(ArrayToXML(arr, o))));
    PrintLn(StrConcat("  XML: ", o));

    // Pruefen und zaehlen
    PrintLn(StrConcat("IsValid('<a>x</a>'):   ", J(IsValid("<a>x</a>"c))));
    PrintLn(StrConcat("IsValid('<a><b></a>'): ", J(IsValid("<a><b></a>"c))));
    PrintLn(StrConcat("IsValid('<a'):         ", J(IsValid("<a"c))));
    PrintLn(StrConcat("IsValid('kein XML'):   ", J(IsValid("kein XML"c))));
    PrintLn(StrConcat("Count('<a><b/><c>x</c></a>'): ", IntToStr(CountElements("<a><b/><c>x</c></a>"c))));
    PrintLn(StrConcat("Count('<a>5 < 7</a>'):        ", IntToStr(CountElements("<a>5 < 7</a>"c))));
    return 0;
}

CreateArrayEntry, Bytes: 13
  Name: titel
  Text: Wert
ArrayToXML, Bytes: 19
  XML: <titel>Wert</titel>
IsValid('<a>x</a>'):   gueltig
IsValid('<a><b></a>'): ungueltig
IsValid('<a'):         ungueltig
IsValid('kein XML'):   ungueltig
Count('<a><b/><c>x</c></a>'): 3
Count('<a>5 < 7</a>'):        1

Die Eintragsstruktur hält ein Element mit Namen und Text — keine Attribute, keine Kinder, keine Liste. Wer mehrere Elemente braucht, legt mehrere Blöcke hintereinander und verwaltet die Offsets selbst.


Prüfen und Zählen

Signatur Was wirklich geprüft wird
IsValid(xml: pchar): bool prüft Wohlgeformtheit: <a>x</a> ist gültig, <a><b></a> (nicht geschlossenes Element) und <a (abgeschnitten) sind es nicht — gemessen mit 1.1.14A, #1422 behoben
CountElements(xml: pchar): int64 Zahl der <, denen kein /, ! oder ? folgt
 
IsValid prüft inzwischen die Struktur (#1422, nachgemessen mit lyxc 1.2.2B): <a> ohne Schluss und </a> allein liefern 0, <a></a> liefert 1, und Tags über Kreuz (<a><b></a>) werden ebenfalls abgewiesen. Auch ein nacktes < im Text (<a>5 < 7</a>) gilt als ungültig — das ist XML-konform, in XML muss dort &lt; stehen.

Was die Prüfung nicht leistet: Namensraum-Auflösung, DTD- oder Schema-Prüfung und die Kontrolle von Attributwerten.

Formatieren

PrettyPrint(input, output, indentSize) rückt Elemente ein und setzt Zeilenumbrüche.

 
PrettyPrint lässt Textinhalte in Ruhe (#1424, nachgemessen mit lyxc 1.2.2B). Aus <a><b>x</b><c/></a> wird mit indentSize = 2:

<a>
  <b>x</b>
  <c/>
</a>


Der Text bleibt in seiner Zeile; frühere Fassungen brachen ihn um und veränderten damit das Dokument. indentSize ist das dritte Argument — ein zu großer Wert (etwa eine Puffergröße) rückt entsprechend absurd weit ein.

Fehlercodes

Konstante Wert Bedeutung
ERR_XML_OK 0 kein Fehler
ERR_XML_INVALID 1 ungültiges Dokument
ERR_XML_EOF 2 Eingabe endet unerwartet
ERR_XML_TAG_MISMATCH 3 öffnendes und schließendes Tag passen nicht
ERR_XML_ATTR_ERROR 4 Attribut nicht lesbar

Keine Funktion der Unit gibt einen dieser Werte zurück. Sie stehen für eigene Fehlerbehandlung bereit; die Unit selbst meldet Fehler über 0 (kein Ergebnis) beziehungsweise gar nicht.


Fallstricke

  • Ausgabepuffer gehören dem Aufrufer und werden nicht auf Größe geprüft. Für EscapeText sind fünf Byte je Eingabezeichen sicher, für EscapeAttribute sechs plus zwei für die Anführungszeichen.
  • Nach Write… selbst nullterminieren (#1425).
  • Attributwerte werden nicht maskiert — WriteElement schreibt sie unverändert zwischen die Anführungszeichen. Ein “ im Wert zerlegt das Dokument.
  • 64 Byte je Attributname und -wert. Längere Angaben werden abgeschnitten, weil die Schleifen bei 64 abbrechen.
  • Keine Kommentare, keine CDATA, keine Namensräume, keine Entitäten — &amp; im Eingabetext bleibt beim Lesen stehen, ein Gegenstück zu EscapeText gibt es nicht.
  • Ein Element je Aufruf. Verschachtelte Strukturen entstehen, indem man WriteElement mehrfach in denselben Puffer schreibt und die Einzugstiefe selbst führt.
  • Für HTML-Text (mit Void-Elementen und ungeschlossenen Tags) ist std.html zuständig — std.xml erwartet gepaarte Tags.

Weiterführend: std.html · std.json · std.string · Standard Library

Quelle: std/xml.lyx (967 Zeilen) · Autor: Andreas Röne · Copyright: 2024–2025 Andreas Röne

Letzte Aktualisierung: 2026-09-05 (#1422 und #1424, nachgemessen mit lyxc 1.2.2B) — IsValid- und PrettyPrint-Kästen auf den behobenen Stand gezogen.

Vorherige letzte Aktualisierung: 2026-09-05 (#1422 und #1424, nachgemessen mit lyxc 1.2.2B) — IsValid- und PrettyPrint-Kästen auf den behobenen Stand gezogen.

Vorherige letzte Aktualisierung: 2026-08-30 — gegen lyxc 1.1.14A nachgemessen: IsValid prüft jetzt die Wohlgeformtheit (#1422), und CountElements zählt ein < in gewöhnlichem Text nicht mehr als Element. Beispielausgabe und Funktionstabelle berichtigt.

Vorherige Aktualisierung: 2026-08-13 — Seite gegen std/xml.lyx überarbeitet: zwei lauffähige Beispiele mit echter Ausgabe, Attribut-Speicherformat (128 Byte je Attribut) und Eintragsformat der Array-Familie dokumentiert, Fallstricke ergänzt. Belegt und als Issue erfasst: IsValid prüft keine Wohlgeformtheit (#1422), ParseString liefert immer 1 und schreibt nichts (#1423), PrettyPrint verändert Textinhalte (#1424), die Write…-Funktionen terminieren nicht (#1425). Geprüft mit lyxc 1.0.21A.

Letzte Aktualisierung: 2026-08-27 — alle vier Mängel gegen lyxc 1.1.11B nachgemessen und als behoben ausgewiesen (#1422, #1423, #1424, #1425); Ergebnisformat von ParseString mit Feldtabelle und laufendem Beispiel ergänzt.

Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).