====== 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.
→ [[lyx_-_programmiersprache:units:html|std.html]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units|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: ''%%%%'' gilt als gültig ([[https://github.com/SEOLizer/LyX-Compiler/issues/1422|#1422]]).
> * ''ParseString'' schreibt in den Ausgabepuffer und meldet ''0'', wenn kein Wurzelelement da ist ([[https://github.com/SEOLizer/LyX-Compiler/issues/1423|#1423]]) — Format siehe unten.
> * ''PrettyPrint'' rührt Textinhalte nicht mehr an ([[https://github.com/SEOLizer/LyX-Compiler/issues/1424|#1424]]).
> * Die drei ''Write…''-Funktionen terminieren ihre Ausgabe ([[https://github.com/SEOLizer/LyX-Compiler/issues/1425|#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("hallo"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'' | '''' |
| ''WriteElement(output, tagName, attrs, attrCount, text, indent): int64'' | Ein Element mit Attributen, Text und Einzug; ohne Text entsteht ''<tag/>''. 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** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1425|#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 \"zitiert\""c, o);
PrintLn(StrConcat("EscapeText: ", o));
EscapeAttribute("Tom & Jerry \"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 & Jerry <b> "zitiert"
EscapeAttribute: "Tom & Jerry <b> "zitiert""
WriteDocument:
Inhalt
WriteElement: - Text
leeres Element:
----
===== 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 ''<name>text</name>'' |
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('x'): ", J(IsValid("x"c))));
PrintLn(StrConcat("IsValid(''): ", J(IsValid(""c))));
PrintLn(StrConcat("IsValid('x'): ", IntToStr(CountElements("x"c))));
PrintLn(StrConcat("Count('5 < 7'): ", IntToStr(CountElements("5 < 7"c))));
return 0;
}
CreateArrayEntry, Bytes: 13
Name: titel
Text: Wert
ArrayToXML, Bytes: 19
XML: Wert
IsValid('x'): gueltig
IsValid(''): ungueltig
IsValid('x'): 3
Count('5 < 7'): 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: ''%%x%%'' ist gültig, ''%%%%'' (nicht geschlossenes Element) und ''%%
> **''IsValid'' prüft inzwischen die Struktur** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1422|#1422]], nachgemessen mit lyxc 1.2.2B): ''%%%%'' ohne Schluss und ''%%%%'' allein liefern **0**, ''%%%%'' liefert **1**, und Tags über Kreuz (''%%%%'') werden ebenfalls abgewiesen. Auch ein nacktes ''<'' im Text (''%%5 < 7%%'') gilt als ungültig — das ist XML-konform, in XML muss dort ''<'' 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** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1424|#1424]], nachgemessen mit lyxc 1.2.2B). Aus ''%%x%%'' wird mit ''indentSize = 2'':
>
>
x
>
> 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** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1425|#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** — ''&'' 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 [[lyx_-_programmiersprache:units:html|std.html]] zuständig — ''std.xml'' erwartet gepaarte Tags.
----
**Weiterführend:** [[lyx_-_programmiersprache:units:html|std.html]] · [[lyx_-_programmiersprache:units:json|std.json]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units|Standard Library]]
**Quelle:** ''std/xml.lyx'' (967 Zeilen) · **Autor:** Andreas Röne · **Copyright:** 2024–2025 Andreas Röne
Letzte Aktualisierung: 2026-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1422|#1422]] und [[https://github.com/SEOLizer/LyX-Compiler/issues/1424|#1424]], nachgemessen mit lyxc 1.2.2B) — ''IsValid''- und ''PrettyPrint''-Kästen auf den behobenen Stand gezogen.
Vorherige letzte Aktualisierung: 2026-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1422|#1422]] und [[https://github.com/SEOLizer/LyX-Compiler/issues/1424|#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 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1422|#1422]]), ''ParseString'' liefert immer ''1'' und schreibt nichts ([[https://github.com/SEOLizer/LyX-Compiler/issues/1423|#1423]]), ''PrettyPrint'' verändert Textinhalte ([[https://github.com/SEOLizer/LyX-Compiler/issues/1424|#1424]]), die ''Write…''-Funktionen terminieren nicht ([[https://github.com/SEOLizer/LyX-Compiler/issues/1425|#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).