Archiv für Kategorie XML

Ein universelles XSL-Stylesheet zum Aufteilen von XML-Dokumenten

Manchmal müssen große XML-Dokumente in kleinere Portionen zerlegt werden. In der Beispielsammlung gibt es dafür ein generisches Stylesheet (xml-aufteilen.xsl). Es teilt XML-Dateien auf der zweiten Ebene (unterhalb des Wurzelementes) in gleich große Gruppen mit bspw. 20 Elementen, umschließt diese Gruppen jeweils mit dem rekonstruierten Wurzelelement und ggfs. führenden Kommentaren und Processing Instructions und gibt die Gruppen als Einzeldateien aus.

Die Implementierung ist pures XSLT 2.0 auf der Grundlage von xsl:for-each-group group-adjacent="…" und xsl:result-document ohne Besonderheiten, deshalb gehe ich hier nicht näher auf den Code ein. Mit dem Stylesheet wird die zu teilende Eingabedatei transformiert, es entstehen eine Protokolldatei sowie die geteilten Dateien. Zahlreiche Parameter erlauben die Steuerung:

Auswahl der auszugebenden Elemente

elemente-pro-datei
(xs:integer) Anzahl der Elemente pro Ausgabedatei. Dieser Wert bestimmt die Größe der Gruppen und damit auch, welche Elemente als Gruppenanfang in Frage kommen. Ist bspw. $elemente-pro-datei gleich 20, dann sind mögliche Gruppenanfänge die Elemente mit der Position 1, 21, 41 usw. Damit wird sichergestellt, dass die Ausgabedateien (mit Ausnahme der letzten) immer vollständige Gruppen enthalten.
Mit einem Wert von 1 wird je Element eine Datei ausgegeben.
erstes-element
(xs:integer) Position des ersten zu kopierenden Elementes. Ohne Angabe eines Wertes wird das erste Element der zweiten Ebene gewählt. Fällt die angegebene Position nicht auf den Anfang einer Gruppe, der sich aus dem Wert für $elemente-pro-datei ergibt, wird das nächste vorhergehende Element, das ein Gruppenanfang ist, gewählt.
letztes-element
(xs:integer) Position des letzten zu kopierenden Elementes. Ohne Angabe eines Wertes wird das letzte Element der zweiten Ebene gewählt. Fällt die angegebene Position nicht auf das Ende einer Gruppe, der sich aus dem Wert für $elemente-pro-datei ergibt, wird das nächst folgende Element, das ein Gruppenende ist, gewählt.

Pfad und Name der Ausgabe-Dateien

Der vollständige Pfad zu den Ausgabedateien setzt sich aus einem für alle Dateien gleichem Pfad zum Ausgabeordner, einem Dateinamen-Präfix, den Zählstellen sowie der Dateityp-Erweiterung zusammen. Aus einer Eingabedatei mit Namen »daten.xml« entstehen bspw. »daten_001.xml«, »daten_002.xml« usw. (bei $elemente-pro-datei = 1) bzw. »daten_001-020.xml«, »daten_021-040.xml« usw. (bei $elemente-pro-datei = 20).

ausgabeordner
(xs:string) Ordner für die Ausgabe der geteilten Dateien als URI, ohne schließenden Schrägstrich. Ohne Angabe eines Ausgabeordners werden die Dateien in den Ordner des Ausgangsdokumentes in einen Unterordner »geteilt« geschrieben.
ausgabedatei-präfix
(xs:string) Präfix für die Ausgabedateien. Ohne Angabe eines Wertes wird der Name der Ausgangsdatei (ohne Dateierweiterung) mit einem nachgestellten Unterstrich verwendet.
ausgabedatei-erweiterung
(xs:string) Dateityp-Erweiterung für die Ausgabedateien. Ohne Angabe eines Wertes wird die Dateierweiterung der Ausgangsdatei verwendet.
picture-string
(xs:string) Formatierung der Zählstellen, als Picture-String (siehe https://wiki.selfhtml.org/wiki/XML/XSL/XPath/Funktionen#format-number.28.29_.28Zahl_in_Zeichenkette_umwandeln.29). Wird kein Wert übergeben, werden führende Nullen entsprechend der Anzahl der Elemente im Ausgangsdokument ausgegeben.

Extras

wiederhole-initiale-nodes
(xs:boolean) Steht dieser Parameter auf true, werden nodes() vor dem ersten Element (Kommentare und/oder Processing-Instructions) in jede Ausgabe-Datei an den Anfang kopiert. Bei false werden entsprechende Knoten nur in die erste Datei kopiert.
Vor dem Wurzelelement stehen oft Lizenzangaben in Kommentaren oder Processing Instructions wie bspw. das xml-model (siehe die W3C Working Group Note Associating Schemas with XML documents). Es kann wünschenswert sein, diese Informationen zu wiederholen.
schuetze-vorhandene-dateien
(xs:boolean) Verhindert, dass vorhandene Dateien überschrieben werden.
Um die Existenz einer Datei zu prüfen, muss die Datei geparst werden. Bei vielen und/oder großen bereits vorhandenen Ausgabedokumenten kann das sehr zeitaufwändig sein. In diesen Fällen empfiehlt es sich, erhaltenswerte Dokumente zu sichern und den Parameter auf false zu setzen.

Um das Stylesheet einfach an eigene Anforderungen anpassen zu können, wurden wichtige Subroutinen als Funktionen und benannte Templates angelegt. Diese können leicht überschrieben werden, um z.B. die auszugebenden Nodes zu filtern (Funktion local:nodes-auswaehlen() oder um ein eigenes Benennungsschema (Funktion local:dateiname()) zu realisieren.

Keine Kommentare

Experten überarbeiten Wikipedia-Artikel zu XML

Auf Grund eines Posts von Tim Bray hat in den letzten Tagen auf der xml-dev-Mailingliste ein spannender Austausch zur Überarbeitung des XML-Artikels in der englischen Wikipedia stattgefunden. Aufregend sind daran zwei Dinge: Erstens war das Who’s Who der XML-Gilde an der Überarbeitung des Artikels beteiligt; dieser Grundlagenartikel ist nun von absoluten Experten verfasst und geprüft (Um ehrlich zu sein: das Review dauert aktuell noch an). Zweitens: Die Diskussion auf der Mailingliste, das Ringen um korrekte Formulierungen ist ebenso lehrreich wie unterhaltsam. Zwei Wermutstropfen bleiben: Die Diskussion wäre auf der Diskussionsseite besser aufgehoben gewesen, und es ist ein Artikel entstanden, der passagenweise von den Wikipedia-Konventionen abweicht – hoffen wir, dass der Inhalt unbeschadet diverse minor edits und Bots übersteht.

Keine Kommentare

Unicode, Encoding, Numeric Character References und Entities

So etwa beim 2. Schritt mit XML stolpert wohl jeder über Probleme mit dem Encoding: Sonderzeichen werden verstümmelt, Zeilenumbrüche verschwinden, Software verschluckt sich. Wo ist das Problem? Eine Datei ist zuerst eine Sequenz von Bytes. Alle Beteiligten müssen diesen Bytes die selbe Bedeutung zumessen, damit die Kommunikation klappt: es ist meist sinnlos, eine Textdatei mit einem Bildeditor zu bearbeiten. Nicht anders ist das bei XML: Probleme entstehen, wenn eine Software die Daten anders interpretiert, als sie vom Sender gemeint waren.

Encoding und Unicode

Technisch ist der Vorgang einfach: am Beginn der XML-Verarbeitung liest ein sogenannter Parser die XML-Daten ein. Zu einem relativ frühen Zeitpunkt wandelt er den Bytestrom in systeminterne normalisierte Zeichen um (Decoding), etwa in Unicode-Codepoints (vereinfacht: Bytes oder Bytegruppen, die Schriftzeichen eindeutig identifizieren). Die weitere Verarbeitung – etwa die Umwandlung in ein Objektmodell und eine XSL-Transformation – findet über diese Codepoints statt. Am Ende der Verarbeitung werden die Objekte und Codepoints wieder in einen Bytestrom – z.B. eine Datei – umgewandelt (Encoding). Der Parser muss also das Encoding der Eingabe kennen, um sie intern sinnvoll verarbeiten zu können; ebenso muss die weiterverarbeitende Software das Encoding der Ausgabe unterstützen.

Ein Encoding ist eine definierte Zuordnung von Bytes und Bytegruppen zu einem bestimmten (Zeichen-) Symbol. Die meisten Encodings sind historisch gewachsen; weit verbreitet sind ASCII, ISO 8859-1 und Windows-1252. Diesen Encodings gemeinsam ist, dass sie nicht alle weltweit vorkommenden Zeichen unterstützen, schon Griechisch oder Kyrillisch liegen außerhalb des definierten Zeichenumfanges.

Unicode wurde entwickelt, um perspektivisch alle sinnvollen Schriftzeichen der Menschheit identifizieren und darstellen zu können. Nach einer bestimmten Systematik wurden Bereiche eingeteilt (sogenannte Planes), in denen wiederum Blöcke (englisch blocks) mehr oder weniger zusammengehörige Zeichen (z.B. eines Schriftsystems) zusammenfassen. Ein einzelnes Zeichen (aber bspw. auch sogenannte Modifier, die andere Zeichen verändern, wie z.B. diakritische Zeichen) wird mit einem Codepoint definiert, d.h. per Definition wird festgelegt, dass ein bestimmtes Zeichen eine bestimmte Position innerhalb eines Blockes hat. Die Position wird einfach durchgezählt. So ist das Zeichen »Ё« das 2. Zeichen im Kyrillisch-Block ab 400, in Unicode-Notierung U+0401. Wichtig: für die Definition des Unicode-Codepoints eines Zeichens haben nur systematische Kriterien eine Rolle gespielt (wobei natürlich über praktische Erwägungen bei der Definition doch technische Gesichtspunkte Einfluss hatten). Mit UTF-8 wurde ein Encoding entwickelt, um alle Unicode-Codepoints in einem Bytestrom darstellen zu können.

Numeric Character References und Character Entity References

Was aber, wenn ein Encoding oder die verwendete Software bestimmte Zeichen nicht verarbeiten oder darstellen kann? Beispielsweise unterstützen nur wenige europäische Zeichensätze Symbole für Chinesisch oder Arabisch. In diesen Fällen können numerische Zeichenreferenzen (engl. Numeric Character References, kurz NCR) verwendet werden. Sie verweisen laut XML-Standard auf einen Unicode-Codepoint. So verweist die numerische Zeichenreferenz A (hexadezimal A) auf U+0041 (den Großbuchstaben »A«). Achtung: Da numerische Zeichenreferenzen auf Unicode verweisen, sind Referenzen auf den Cp-1252-(Windows-) Bereich zwischen 128 und 159 (hexadezimal 80 und 9F) ungültig (auch wenn viele Programme – wie Webbrowser – diese fehlerhaften Referenzen stillschweigend korrigieren).

Ein anderer Fall sind Character Entity References, umgangsprachlich meist Entities genannt. In SGML-basierten Sprachen wie HTML und XML können Character Entity References für Zeichen und Zeichengruppen vorab definiert werden, die dann im Text an Stelle der referenzierten Zeichen verwendet werden. So steht in HTML ü für das kleine »ü«. Bei XML müssen Character Entity References (mit Ausnahme der vordefinierten &, <, > und ") im Dokument oder in einer externen DTD definiert werden.

Die Behandlung von Numeric Character References und Character Entity References ist Bestandteil der SGML-, HTML- und XML-Standards. Damit obliegt sie dem entsprechenden Parser. Unicode und UTF-8 kennen diese Konstrukte nicht.

Probleme

Die häufigsten Probleme liegen jetzt auf der Hand:

  • Die Software erkennt das Encoding nicht richtig. Meist wird in diesen Fällen ein Standard-Encoding verwendet, dass nicht unbedingt jenes des Dokumentes sein muss.
  • Das Dokument hat ein anderes Encoding, als deklariert wurde.
  • Das Dokument verwendet Zeichen, die im deklarierten Encoding nicht vorkommen. Hier ist in Windows-Umgebungen ein häufiger Fehler, das als Encoding ISO 8859-1 oder UTF-8 angegeben wird, im Dokument aber Zeichen oder numerische Zeichenreferenzen aus dem Cp-1252-Bereich zwischen 128 und 159 (hexadezimal 80 und 9F) verwendet werden.
  • Die Software kann bestimmte Codepoints nicht darstellen und verwendet dafür Ersatzzeichen wie das Fragezeichen.
  • Die Software kann auf die Definition von Character Entity References nicht zugreifen, etwa weil die DTD nicht angegeben wurde oder nicht verfügbar ist.

Tückisch ist, dass viele Anwendungen bei diesen Problemen die Arbeit nicht verweigern (können), weil ja zumindest die Zeichen aus dem ASCII-Bereich (kleiner 128) ihren Dienst leisten. In der Folge entstehen Ausgabedokumente mit falsche Zeichen, oder gespeicherte Dokumente werden irreparabel beschädigt. Deshalb ist es besser, auf eine korrekte Abstimmung des Encodings zu achten, als zweifelhafte Workarounds zu bemühen.

Quellen

Standard: http://www.w3.org/TR/xml/#sec-references

Wikipedia (zum Lesen nicht wirklich zu empfehlen): http://de.wikipedia.org/wiki/Unicode, http://de.wikipedia.org/wiki/UTF-8, http://de.wikipedia.org/wiki/ISO_8859-1, http://de.wikipedia.org/wiki/Entit%C3%A4ten_in_Auszeichnungssprachen

Nachtrag (englisch, dafür gut lesbar): http://lachy.id.au/log/2005/10/char-refs

Nachtrag II: Zu den hier nur kurz erwähnten Zeilenumbrüchen siehe Encoding und Zeilenumbrüche ändern mit XSLT

Nachtrag III: Sehr gute Erklärung zu Unicode und Encoding: Joel Spolsky: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) (englisch)

Keine Kommentare