XSLT/XPath: arithmetische Operationen mit einer Leersequenz
Verfasst von Stf unter XSLT und XPath am 17. Juli 2010
Neues aus der Serie »Man lernt nie aus…«: Ist bei arithmetischen Operationen einer der Operanden die empty sequence, so ist das Ergebnis bei XSLT/XPath 1.0 NaN (»not a number«), bei XSLT/XPath 2.0 aber die empty sequence. Dieses Verhalten ist im XPath-Standard unter 3.4 Arithmetic Expressions definiert.
Ich stand vor dem Problem, eine Variable, die entweder eine xs:decimal-Zahl oder aber auch die Leersequenz enthält, in eine gültige Instanz von xs:decimal umzuwandeln, d.h. statt der Leersequenz sollte 0 geliefert werden. Es ist gängige Programmiertechnik, eine Leersequenz durch Anhängen eines Leerstrings – z.B. xs:string( ( (), '') ) – in eine gültige Instanz von xs:string (d.h. einen Leerstring) umzuwandeln. Die analoge Technik – Addieren einer Null zu einer Leersequenz – funktioniert aber wegen des im Standard definierten Verhaltens nicht. Abhilfe schafft bei XSLT 2.0 eine spezielle Funktion:
<xsl:function name="xsb:force-cast-to-integer" as="xs:decimal"> <xsl:param name="input" as="xs:string?"/> <xsl:choose> <xsl:when test="$input castable as xs:decimal"> <xsl:sequence select="xs:decimal($input)"/> </xsl:when> <xsl:otherwise> <xsl:sequence select="0"/> </xsl:otherwise> </xsl:choose> </xsl:function>
oder kürzer:
<xsl:function name="xsb:force-cast-to-integer" as="xs:decimal"> <xsl:param name="input" as="xs:string?"/> <xsl:sequence select="if ($input castable as xs:decimal) then xs:decimal($input) else 0"/> </xsl:function>
Außer bei Leerstring und Leersequenz gibt diese Funktion auch bei nicht konvertierbaren Strings (wie z.B. römischen Zahlen) 0 zurück (was bei mir häufig das gewünschte Verhalten ist), aber die Beschränkung auf Leerstring und Leersequenz lässt sich einfach durch Ersetzen von $input castable as xs:decimal mit normalize-space($input) erzielen.
Nachtrag:
Thomas Meinicke merkte zum Rechnen mit Leersequenzen an, dass man zuerst mit exists() oder empty() prüfen kann, ob eine Sequenz leer ist, um dann den resultierenden Wahrheitswert in xs:decimal zu casten, etwa so: xs:decimal(exists( () ) ). Achtung: Dabei wird die Leersequenz zu 0, während der Leerstring zu 1 wird.
Java in XSLT verwenden
Verfasst von Stf unter Java, XSLT und XPath am 9. Juli 2010
Es gibt immer wieder Programmieraufgaben, bei denen XSLT allein zur Lösung nicht ausreicht. In diesen Fällen kann – vorausgesetzt, dass der XSLT-Prozessor mitspielt – die Einbindung von Java helfen. XSLT bietet dafür zwei Mechanismen: Erweiterungsfunktionen (Extension Functions) und Erweiterungsbefehle (Extension Instructions, in XSLT 1.0 als Extension Elements bezeichnet). Im Folgenden werden nur Erweiterungsfunktionen betrachtet.
Nutzung von Standard-Java-Methoden
In einem früheren Post habe ich ein Verfahren beschrieben, um mit Saxon-Erweiterungsfunktionen die Existenz einer Datei zu testen. Natürlich lässt sich diese Aufgabe auch mit Java erledigen:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:test="something" xmlns:java-file="java:java.io.File" xmlns:java-uri="java:java.net.URI"> <xsl:function name="test:file-exists" as="xs:boolean"> <xsl:param name="fileURL" as="xs:string"/> <xsl:sequence select="java-file:exists(java-file:new(java-uri:new($fileURL)))"/> </xsl:function> </xsl:stylesheet>
Die Funktion test:file-exists nutzt die Methode exists() aus der Klasse java.io.File. Dazu wird in der xsl:stylesheet-Deklaration der Namespace java.io.File (der Klassenname) definiert und an das Präfix java-file gebunden. In der Zeile <xsl:sequence ... wird dann mit java-file:new(...) eine neue Instanz erzeugt und mit java-file:exists(...) die Methode aufgerufen. Die merkwürdige Syntax ist dem Umstand geschuldet, dass der Aufruf von Java wie eine XPath-Funktion aussehen soll, um problemlos mit XSLT verarbeitet werden zu können.
Die Klasse java.net.URI verwende ich zusätzlich, um den übergebenen String auf Gültigkeit zu überprüfen – bei einem ungültigen String schlägt schon die Erzeugung der URI fehl. Zudem erzwingt der Konstruktor von java.net.URI die Angabe einer absoluten URI, was in der Praxis hilft, Programmierfehler zu vermeiden. Relative Pfade lassen sich einfach mit der XPath-Funktion resolve-uri() – ggfs. in Verbindung mit base-uri() – in absolute Pfade umwandeln. Die Typung des Funktionsparameters fileURL als xs:anyURI würde hier übrigens nicht wirklich weiter helfen, da xs:anyURI per Standard nur recht schwache Gültigkeitskriterien hat. Wenn dieser zusätzliche Test nicht benötigt wird, kann das File-Objekt aber auch direkt aus dem Funktionsparameter erzeugt werden.
Das vollständige Stylesheet steht in der Beispielsammlung zum Download bereit.
Voraussetzungen und Konfiguration
Um Java in XSLT verwenden zu können, muss der jeweilige XSLT-Prozessor dies unterstützen. Nachdem Michael Kay neue Saxon-Pakete zusammengestellt hat, bietet sich folgendes Bild: Saxon-B und Saxon-SA bis einschließlich Version 9.1 unterstützen die Einbindung von Java-Funktionen, ab Version 9.2 funktioniert dies nur noch mit den kostenpflichtigen PE- und EE-Versionen. Bei AltovaXML hatte ich mit dem Build vom 23.10.2009 sowie der Version 2010 rel. 3 sp 1 Erfolg, allerdings musste ich nach der Fehlermeldung »JVM dll kann nicht geladen werden« bzw. »Can’t load JVM DLL« erst ein JDK installieren und die Path-Umgebungsvariable um das bin-Verzeichnis im JDK ergänzen.
Ein weiteres Beispiel
Im letzten Blog-Beitrag wurde die Ausgabe von Binärdaten in eine Datei angesprochen. Eine erste Java-Lösung sieht so aus:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:test="something" xmlns:java-file="java:java.io.File" xmlns:java-fos="java:java.io.FileOutputStream" xmlns:java-uri="java:java.net.URI" version="2.0"> <xsl:function name="test:write-hexBinary-to-file"> <xsl:param name="hexBin" as="xs:hexBinary?"/> <xsl:param name="OutputFileURI" as="xs:anyURI"/> <xsl:variable name="fostream" select="java-fos:new(java-file:new(java-uri:new($OutputFileURI) ) )"/> <xsl:value-of select="java-fos:write($fostream, test:tokenize-HexBinary($hexBin) )"/> </xsl:function> […] </xsl:stylesheet>
Zuerst wird ein FileOutputStream-Objekt erzeugt und einer Variablen zugewiesen. Danach wird die write()-Methode des Objektes aufgerufen. Interessant ist die Syntax dieses Methodenaufrufes: der scheinbaren Funktion java-fos:write() wird als erstes Argument das Objekt, dessen Methode aufgerufen werden soll, übergeben.
Die Umwandlung des HexBin-Strings in eine Sequenz von Bytes habe ich in die XSLT-Funktion test:tokenize-HexBinary samt zweier Hilfsfunktionen ausgelagert, die Details lassen sich im Beispiel-Stylesheet nachschlagen.
Einbindung externer Klassen
Das FileOutputStream-Beispiel funktioniert zwar, aber es ist eine ziemliche XSLT-Akrobatik für das Umwandeln von HexBinary in Bytes notwendig. Wenn man schon Java benutzt, kann man das Problem auch gleich ganz an Java delegieren. Unter Verwendung einer Lösung von Dave L. habe ich diese Klasse geschrieben:
package org.expedimentum.example.java; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class HexBinaryFileWriter { public void write (String hexbinary, File file) throws FileNotFoundException, IOException { FileOutputStream fos = new FileOutputStream(file); for (int i = 0; i < hexbinary.length(); i += 2) { fos.write( (Character.digit(hexbinary.charAt(i), 16) << 4) + Character.digit(hexbinary.charAt(i+1), 16)); } } }
Diese Klasse ist eine ganz normale Java-Klasse, sie beinhaltet keinen XSLT-bezogenen Code. Sie kann nun wie in den obigen Beispielen beschrieben in ein Stylesheet eingebunden werden:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:test="something" xmlns:java-hbfw="java:org.expedimentum.example.java.HexBinaryFileWriter" xmlns:java-file="java:java.io.File" xmlns:java-uri="java:java.net.URI" version="2.0"> <xsl:function name="test:write-hexBinary-to-file"> <xsl:param name="hexBin" as="xs:hexBinary?"/> <xsl:param name="OutputFileURI" as="xs:anyURI"/> <xsl:variable name="HexBinaryFileWriter" select="java-hbfw:new()"/> <xsl:value-of select="java-hbfw:write($HexBinaryFileWriter, string($hexBin), java-file:new(java-uri:new($OutputFileURI) ) )"/> </xsl:function> </xsl:stylesheet>
Unter <OxygenXML/> und Saxon muss das jar-File mit der HexBinaryFileWriter-Klasse im CLASSPATH liegen. Bei Saxon darf die Transformation nicht mit java -jar ... gestartet werden, vielmehr muss der Aufruf über java -cp ... net.sf.saxon.Transform erfolgen. AltovaXML wünscht die Übergabe des Pfades zum jar in der Deklaration des Namespace, was dann bspw. so aussehen könnte: xmlns:java-hbfw="java:org.expedimentum.example.java.HexBinaryFileWriter?path=jar:file:///C:/example/Java/dist/Beispiele.jar!/".
Der Quelltext der Klasse und ein jar liegen in der Beispielsammlung, ebenso das vollständige Stylesheet.
Noch ein Tipp zum Schluss: einfacher als das Schreiben, Einbinden und Warten von Java-Erweiterungen dürfte in vielen Fällen die Nutzung prozessorspezifischer Erweiterungsfunktionen – wie sie z.B. Saxon und AltovaXML anbieten – oder von XSLT-Bibliotheken wie EXSLT sein.
Weblinks
- XSL Transformations (XSLT) Version 2.0. 18 Extensibility and Fallback – http://www.w3.org/TR/xslt20/#extension (englisch)
- XSLT blooms with Java. Use Java in your stylesheets when XSLT won’t do the trick – http://www.javaworld.com/javaworld/jw-12-2001/jw-1221-xslt.html?page=1 (englisch)
- Extending XSLT with Java – http://cafeconleche.org/books/xmljava/chapters/ch17s03.html (englisch)
- Saxonica: XSLT and XQuery Processing: Writing reflexive extension functions in Java – http://www.saxonica.com/documentation/extensibility/functions.html (englisch)
- AltovaXML 2010: Java Extension Functions – http://manual.altova.com/AltovaXML/index.html?xextjava.htm (englisch)
Binärdateien mit Saxon schreiben
Verfasst von Stf unter Beispiele, Java, XSLT und XPath am 28. Juni 2010
XSLT bietet von Haus aus keine Möglichkeit zur Ausgabe von Binärdateien wie Bildern. Aber zumindest Saxon-PE/Saxon-EE ab Version 9.2 bietet eine Erweiterung, um diese Beschränkung zu umgehen. Nach einigen Tests ist es mir gelungen, sowohl jpeg als auch png zu schreiben. Das Beispiel-Stylesheet hat folgenden Abschnitt:
<xsl:variable name="image" as="xs:hexBinary">89504E470D0A1A0A0000000D494844520000000D0000000F0403000000CD0D5844000000017352474200AECE1CE900000018504C544562129D80418A8851829E7972B09266DCD946F4FF39FEFFFC482C1765000000564944415408D725CD2B12C0200C04D030133C0A5D55DD2354A1A3D069A17BFF237403316FF217AC70190ABCD905A5E36ED4CE2FB18EA14F0E51AEB6B484AD6E0F6ED0A95643AB53B1EF94EEC2842517363912F3EBDF0F99462041E4FA77630000000049454E44AE426082</xsl:variable> <xsl:result-document method="text" encoding="iso-8859-1" href="x.png" saxon:recognize-binary="yes"> <xsl:processing-instruction name="hex" select="$image"/> </xsl:result-document>
Ich hatte auf einem Mac mit <OxygenXML/> kein Glück bei der expliziten Angabe des Encodings, etwa hex.utf8, aber vielleicht habe ich nicht konsequent genug probiert.
Das vollständige Stylesheet steht unter http://www.expedimentum.org/example/xslt/binaerdaten-ausgabe.xsl zum Download bereit.
Nachtrag
Wenn man schon Erweiterungs-Funktionen bemüht, kann man die Arbeit auch gleich Java erledigen lassen. Damit ist man nicht mehr auf eine der »großen« Saxon-Versionen beschränkt; auch AltovaXML und Saxon-B 8.9 und 9.1 lassen sich zur Mitarbeit überreden. Ich habe zwei Varianten entwickelt: binaerdaten-ausgabe_java.xsl »borgt« sich nur den FileOutputStream von Java und wandelt den HexBinary-String per XSLT in eine Sequenz von Bytes um. binaerdaten-ausgabe_java_extern.xsl bedient sich der externen Klasse HexBinaryFileWriter, die die komplette Konvertierung und Ausgabe abwickelt. Die Erklärung folgt im nächsten Blog-Beitrag.
Include mehrerer Pattern in Schematron
Verfasst von Stf unter Schematron, XML-Validierung am 17. Juni 2010
Schematron bringt einen recht simplen Mechanismus zum Einbinden externen Schemata mit: das Element <sch:include/> bindet ein externes XML-Dokument ein, und zwar genau an der Stelle, wo <sch:include/> im Code steht. Das partielles Überschreiben oder gezielte Einbinden einzelner Pattern und Rules ist nicht möglich. Dies hat zwei Folgen:
- Da unterhalb von
<sch:schema/>kein weiteres<sch:schema/>eingebunden werden darf, kann die eingebundene Datei nicht alleinstehend zum Validieren benutzt werden. - Da
<sch:pattern/>direkt unterhalb von<sch:schema/>stehen muss (es gibt in Schematron kein klammerndes Element wie etwa<define/>in RelaxNG), können mehrere Pattern nur eingebunden werden, wenn sie in der eingebundenen Datei als Wurzelelemente direkt nebeneinander stehen. Eine solche Datei ist aber kein wohlgeformtes XML, folgerichtig verweigert Schematron den Include.
Ein Thread auf der DSDL-Mailingliste und eine Anfrage auf der XML-DEV-Mailingliste ergaben zwar, dass das Problem bekannt ist, die vorgeschlagenen Lösungen sollen aber erst in zukünftigen Versionen von Schematron festgezurrt werden, und sie funktionieren jetzt noch nicht mit <OxygenXML/>. Deshalb entschied ich mich, auf die im grundlegenden XML-Standard definierte Einbindung externer Entities zurückzugreifen. Dieser Workaround sieht so aus:
Basis-Schematron:
<!DOCTYPE schema [ <!ENTITY basic-pattern SYSTEM "basic-pattern.ent"> ]> <schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"> &basic-pattern; </schema>
eingebundene Entity-Datei (basic-pattern.ent):
<pattern xmlns="http://purl.oclc.org/dsdl/schematron"> <!-- some rule --> </pattern> <pattern xmlns="http://purl.oclc.org/dsdl/schematron"> <!-- some rule --> </pattern> <!-- add some more pattern here -->
Diese Datei ist kein wohlgeformtes XML, weil sie mehrere Wurzelelemente hat, sie wird aber nur eingebunden in andere Dateien verwendet – die dann wohlgeformt sind. In der Praxis wird man wohl einfach ein Schematron entwickeln und die Includes dann per Cut&Paste in die Entity-Datei verschieben, so dass die fehlende Wohlgeformtheit kein Nachteil ist.
erweitertes Schematron:
<!DOCTYPE schema [ <!ENTITY basic-pattern SYSTEM "basic-pattern.ent"> ]> <schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"> &basic-pattern; <!-- add some more pattern here --> </schema>
Dieser Workaround hat verschiedene Vorteile:
- Externe Entity-Dateien sind konform zum XML- und Schematron-Standard.
- Die einbindenden Dateien lassen sich zusammen mit den eingebundenen Dateien validieren.
- Es sind keine Änderungen an etablierten Werkzeugen und Workflows notwendig (so ist es mir mit flüchtiger Recherche nicht gelungen, <OxygenXML/> andere Schematron-Stylesheets unterzuschieben).
Ich habe die drei Beispieldateien unter http://www.expedimentum.org/example/Schematron/ abgelegt.
XSLT: import precedence und priority
Verfasst von Stf unter XSLT und XPath am 17. April 2010
Bei der Fehlersuche in einem Stylesheet bin ich heute auf eine überraschende Erkenntnis gestoßen: die import precedence bestimmt vor und unabhängig von der priority, welches matching template angewendet wird. Damit werden allgemeine Regeln im importierenden Stylesheets ohne Rücksicht auf spezielle Regeln in importierten Stylesheets ausgeführt. Ist das nicht gewünscht, lässt sich mit <xsl:next-match/> oder <xsl:apply-imports/> die Ausführung der importierten Templates ggfs. erzwingen. Alternativ lassen sich Stylesheets auch mit <xsl:include/> einbinden.
CiteProc
Kürzlich wurde mein Artikel zu CiteProc in der deutschsprachigen Wikipedia gelöscht, weil die Software nicht den einschlägigen Relevanzkriterien entspricht. Ich bin mir sicher, dass so unbedarfte Wikipedia-Leser prima vor den Fährnissen bibliografischer Formatierung geschützt sind. Da ich hier aber darauf keine Rücksicht nehmen muss, habe ich den Artikel in das Glossar eingebaut. SCNR.
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.
XSLT-Dokumentation mit XSLStyle™
Verfasst von Stf unter Beispiele, XSLT und XPath am 3. Mai 2009
Leider gibt es für XSLT keine standardisierten oder auch nur weitverbreiteten Regeln für die Dokumentation der Stylesheets. Ein Grund dafür könnte sein, dass einfach zu bedienende Werkzeuge nicht verfügbar sind. Für die Bespiele auf dieser Website habe ich mir noch einmal ein paar Ansätze angeschaut und mich dann für XSLStyle™ von G. Ken Holman entschieden, weil a) dieses Tool als XSL-Transformation direkt auf das Stylesheet selbst angewandt werden kann und b) XSLStyle™ auch noch ein paar Tests auf guten Programmierstil durchführt.
Die Installation ist recht einfach: zip-Datei herunterladen, in einem beliebigen Verzeichnis entpacken, fertig. XSLStyle™ kann über die Kommandozeile auf das zu dokumentierende Stylesheet angewandt werden, bequemer ist natürlich, in <OxygenXML/> ein Transformations-Szenario einzurichten.Die Dokumentation erfolgt über Elemente im http://www.CraneSoftwrights.com/ns/xslstyle-Namensraum, die unmittelbar vor dem zu dokumentierenden XSL-Element stehen. XSLStyle™ kann bei fehlender Dokumentation warnen. Weil bei mir das von XSLStyle™ vorgegebene Präfix xs: immer für den http://www.w3.org/2001/XMLSchema-Namensraum steht, habe ich das Präfix doc: verwendet.
Innerhalb der XSLStyle™-Elemente kann die Dokumentation in beliebigem Markup erfolgen, G. Ken Holman gibt Stylesheets für DocBook und DITA mit. Ich habe mich pragmatisch für DocBook entschieden, weil ich damit aus einem früheren Projekt etwas Erfahrung habe. Es steht ein großer Teil des DocBook-Vokabulars zur Verfügung; nachdem meine Experimente mit einem recht elaboriertem Markup (wie refentry und funcsynopsis) primär aufgeblähten Code hervorgebracht haben, schien mir eine stark vereinfachte Version jedoch die bessere Variante. Der Code aus einem der letzten Posts zum Dateien-Testen sieht mit Dokumentation so aus:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:misc="http://www.expedimentum.org/XSLT/Misc" xmlns:saxon="http://saxon.sf.net/" xmlns:doc="http://www.CraneSoftwrights.com/ns/xslstyle" xmlns:docv="http://www.CraneSoftwrights.com/ns/xslstyle/vocabulary" exclude-result-prefixes="doc docv" extension-element-prefixes="saxon" > <!-- --> <doc:doc filename="files.xsl" internal-ns="docv" global-ns="doc misc" vocabulary="DocBook" info="$Revision: 29 $, $Date: 2009-05-03 02:11:03 +0200 (So, 03 Mai 2009) $"> <doc:title>Dateien und Dateisystem</doc:title> <para>Dieses Stylesheet enthält Funktionen rund um das Dateisystem.</para> <para>Dieses Stylesheet unterliegt mangels Schöpungshöhe keinen urheberrechtlichen Beschränkungen.</para> <revhistory> <revision> <revnumber>0.28</revnumber> <date>2008-05-03</date> <authorinitials>Stf</authorinitials> <revremark>erste Version mit Dokumentation</revremark> </revision> </revhistory> </doc:doc> <!-- --> <!-- __________ misc:file-exist() __________ --> <doc:function> <doc:param name="href"><para>Pfad zur zu überprüfenden Datei (URI-encoded)</para></doc:param> <para>Diese Funktion überprüft, ob die angegebene Datei vorhanden ist. Das Ergebnis ist ein Wahrheitswert (<code>xs:boolean</code>, <code>true()</code> oder <code>false()</code>). <emphasis role="bold">Achtung!</emphasis> Diese Funktion erfordert bei Ausführung Saxon, da die Saxon-spezifische Erweiterung <code>saxon:file-last-modified()</code> verwendet wird.</para> </doc:function> <xsl:function name="misc:file-exists" as="xs:boolean"> <xsl:param name="href" as="xs:string?"/> <xsl:value-of select="boolean(normalize-space(string(saxon:file-last-modified($href))))"/> </xsl:function> <!-- --> </xsl:stylesheet>
Damit XSLStyle™ eine Dokumentation ausgibt, müssen der http://www.CraneSoftwrights.com/ns/xslstyle und ein weiter Namensraum für interne Zwecke (http://www.CraneSoftwrights.com/ns/xslstyle/vocabulary) im zu dokumentierenden Stylesheet deklariert werden. Außerdem erwartet XSLStyle™ am Anfang der Dokumentation ein <doc:doc/>-Element mit Basisinformationen, fehlt es (oder auch nur einzelne Attribute), gibt es bei mir merkwürdige Effekte.
Das vollständige Beispiel habe ich unter http://www.expedimentum.org/example/xslt/files.xsl abgelegt, die resultierende Dokumentation liegt unter http://www.expedimentum.org/example/xslt/doc/files.html. Hier bzw. hier gibt es ein etwas umfangreicheres Beispiel mit importierten Stylesheets.
Zum Schluss folgt noch eine Sammlung zufälliger Links zur XSL-Dokumentation, die bei der Recherche entstanden ist. Falls XSLStyle™ nicht passt, findet sich sicher eine geignetere Lösung. Mir hat XSLTdoc relativ gut gefallen, und mit einem Ant-Script lässt sich auch die umständliche Handhabung bändigen, leider lässt es sich nicht direkt als Transformation auf das zu dokumentierende Stylesheet anwenden.
- http://www.dpawson.co.uk/xsl/sect2/documentation.html: XSL-Dokumentation in den XSLT FAQ (englisch)
- http://www.bacman.net/apps/XSLdoc/: XSLdoc, ähnlich JavaDoc (englisch)
- http://xsltsl.sourceforge.net/docbook-extensions.html: DocBook Extensions For XSLT Stylesheet Documentation – Stylesheets in DocBook dokumentieren (englisch)
- http://www.linkwerk.com/pub/xslt/lib/xsltdoc/: xsltdoc — Literate XSLT Programming based on Namespaces (deutsch)
- http://www.pnp-software.com/XSLTdoc/: XSLTdoc – A Code Documentation Tool for XSLT (englisch)
- http://www.devx.com/getHelpOn/10MinuteSolution/20343/0/page/1: Document Your XSLT – eine ähnliche Lösung wie XSLTDoc (englisch)
- http://www.kanzaki.com/parts/xsltdoc.xsl: XSLT Stylesheet description stylesheet – ein weiteres Stylesheet, mit generischer Dokumentation
siehe auch: Florent Georges: XSLStyle and oXygen (englisch)
Encoding und Zeilenumbrüche ändern mit XSLT
Verfasst von Stf unter Beispiele, Grundlagen, XSLT und XPath am 26. April 2009
Beim Datenaustausch mit XML entsteht immer wieder das Problem, dass eine bestimmte Anwendung nur Daten in einem bestimmten Encoding »versteht«, es ist also gelegentlich eine Konvertierung des Encodings notwendig. Ich verwende dazu eine »Identity Transformation«, die bis auf das Encoding tatsächlich überhaupt keine Änderungen vornimmt:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <!-- --> <xsl:output encoding="Windows-1252" indent="no" method="xml"/> <!--<xsl:output encoding="UTF-8" indent="no" method="xml"/>--> <!--<xsl:output encoding="UTF-16" indent="no" method="xml"/>--> <!--<xsl:output encoding="ISO-8859-1" indent="no" method="xml"/>--> <!-- --> <xsl:preserve-space elements="*"/> <!-- --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- --> </xsl:stylesheet>
In XSLT kann man das Encoding des Output-Dokumentes mit <xsl:output encoding="xxx"/> festlegen. Im obigen Beispiel habe ich die gängigen Encodings vorgegeben, zur Anwendung müssen nur die jeweilige Zeile vom Kommentar befreit und die Zeile auskommentiert werden.
Die unterstützen Encodings sind vom jeweiligen XSLT-Prozessor und im Fall von Saxon zudem von der darunter liegenden Java-Installation abhängig. Obwohl laut XML-Standard nur UTF-8 und UTF-16 unterstützt werden müssen, kommen viele XSLT-Prozessoren auch mit ISO 8859-1, Windows-1252 und anderen zurecht.
Zeilenumbrüche
Im vorigen Post habe ich das Problem der falschen Zeilenumbrüche angesprochen, aber keine Lösung angeboten. Die folgt nun hier, allerdings muss ich dazu etwas weiter ausholen:
Zeilenumbrüche sind sogenannte Steuerzeichen, d.h. Zeichen, die normalerweise nicht dargestellt werden, sondern die Darstellung steuern. Historisch haben sich drei Varianten etabliert: Unix verwendet das Steuerzeichen LF (engl. line feed, hexadezimal &0A;), der Mac bis Mac OS 9 CR (engl. carriage return, hex &0D;) und Windows die Sequenz CRLF (&0A;&0D;). Unicode und alle Encodings kennen beide Zeichen, allerding müssen laut XML-Standard bei der Verarbeitung von XML-Dokumenten CR und CRLF zu LF normalisiert werden, so dass ausgegebene Dokumente normalerweise Unix-Zeilenumbrüche enthalten.
Um trotzdem Windows-Zeilenumbrüche in der Ausgabe zu erhalten, muss der XSLT-Prozessor bei der Ausgabe LF wieder in CRLF umwandeln. Soweit ich weiß, gibt es in Saxon keine Möglichkeit dazu, allerdings schreibt Microsofts msxsl.exe Windows-Zeilenumbrüche. <OxygenXML/> erlaubt in der Konfiguration von Transformations-Szenarien die Auswahl von MSXML4.0 als Transformator (sic!). Einzige Einschränkung: Microsoft unterstützt nur XSLT in der Version 1.0.
Ich habe das Stylesheet im Beispiel-Ordner hinterlegt und das Beispieldokument um Testfälle ergänzt. Auch die <OxygenXML/>-Projekt-Datei liegt in einer ergänzten Version vor.
Unicode, Encoding, Numeric Character References und Entities
Verfasst von Stf unter Grundlagen, XML am 21. April 2009
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)

