Archiv für Kategorie Grundlagen
XML Schema: complexType mit simpleContent
Verfasst von Stf unter Grundlagen, XML Schema, XML-Validierung am 4. Dezember 2011
Gelegentlich steht man vor dem Problem, in XML Schema ein Element mit einem oder mehreren Attributen und einem in irgendeiner Form beschränkten simpleContent (Text, Zahlen, URIs; aber keine Elemente) zu definieren. Die Syntax von XML Schema dazu ist wenig intuitiv:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="MyElement"> <xs:complexType> <xs:simpleContent> <xs:extension base="MySimpleElementType"> <xs:attribute name="MyAttribute" type="MySimpleAttributeType"/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:simpleType name="MySimpleElementType"> <xs:restriction base="xs:string"> <xs:enumeration value="Value_A"/> <xs:enumeration value="Value_B"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="MySimpleAttributeType"> <xs:restriction base="xs:string"> <xs:enumeration value="AttributeContent_A"/> <xs:enumeration value="AttributeContent_B"/> </xs:restriction> </xs:simpleType> </xs:schema>
Es handelt sich um einen complexType (wegen des Attributes) mit simpleContent (hier xs:string mit aufgezählten gültigen Werten). Wenig intuitiv ist, dass ein simpleType mit einem Attribut erweitert wird und so faktisch ein complexContent wird, aber trotzdem innerhalb von simpleContent steht. Der Schlüssel ist sicher die Unterscheidung von Type und Content, denn der Inhalt des Elements ist ja immer noch einfach.
Das Schema und ein Instanzdokument habe ich in der Beispielsammlung abgelegt.
AltovaXML in OxygenXML einbinden
Verfasst von Stf unter Allgemeines, Grundlagen am 30. Mai 2011
Um AltovaXML in OxygenXML nutzen zu können, muss man diesen XSLT-Prozessor als »Custom Engine« einrichten. So geht’s:
Unter Einstellungen/XML/XSLT-FO-XQuery/Custom Engines einen neuen Prozessor anlegen:
Als Prozessortyp ist »XSLT« vorausgewählt und richtig. Name und Beschreibung eingeben und dann in das Feld Kommandozeile »"C:\Program Files (x86)\Altova\AltovaXML2011\AltovaXML.exe" /xslt2 ${xsl} /in ${xml} /out ${out}« eintragen (Pfad zu AltovaXML bitte an lokale Gegebenheiten anpassen).
Speichern, fertig. In den Transformationsszenarien kann jetzt der neu angelegte Prozessor ausgewählt werden:
Leider kann OxygenXML keine Parameter oder initiale Modes bzw. initiale Templates an den so erzeugten Prozessor übergeben. Ein Workaround ist, für definierte Fälle eigene Prozessoren anzulegen. Für den ad-hoc-Selbsttest der XSL-SB sieht das so aus:
Der erste Teil der Kommandozeile »"C:\Program Files (x86)\Altova\AltovaXML2011\AltovaXML.exe" /xslt2 ${xsl} /in ${xml} /out ${out} /m internals.self-test -param internals.logging-level="'DEBUG'" -param internals.errors.die-on-critical-errors="'no'"« ist identisch zu oben, initialer Mode und Parameter werden zusätzlich hart codiert übergeben. Hier sollten die Entwickler von OxygenXML gelegentlich einmal nachbessern, es kann ja nicht so schwer sein, einen benannten Parameter als benanntes Makro anzubieten.
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)
Die einfachste XSL-Transformation
Verfasst von Stf unter Beispiele, Grundlagen, XSLT und XPath am 29. März 2009
Meine ersten Gehversuche mit XSLT hatten das Ziel, kleine Änderungen an XML-Dokumenten vorzunehmen: einzelne Elemente löschen, Werte neu berechnen und ähnliches. Die Lösung dafür ist die sogenannte »Identity Transformation«:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <!-- --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- --> </xsl:stylesheet>
Dieses Stylesheet kopiert das Eingabedokument vollständig in das Ausgabedokument. Das einzige Template matcht alle Attribute (@*) und alle Knoten (node(), das sind Elemente, Text, Kommentare und sogenannte Processing Instructions).
Wie kann man nun Änderungen am Dokument vornehmen? Ganz einfach: Es werden zusätzliche Templates eingefügt, die nur die zu ändernden Knoten beeinflussen. Sollen zum Beispiel aus einem XHTML-Dokument alle <code/>-Elemente gelöscht werden, hilft folgendes Stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml" > <!-- --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- --> <xsl:template match="xhtml:code"/> <!-- --> </xsl:stylesheet>
Leider wird jetzt der enthaltene Text ebenfalls gelöscht. Wenn die Kind-Knoten (z.B. text()-Knoten) erhalten werden sollen, muss für diese die allgemeine Kopierregel angewendet werden, am einfachsten durch ein <xsl:apply-templates/>:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml" > <!-- --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- --> <xsl:template match="xhtml:code"> <xsl:apply-templates/> </xsl:template> <!-- --> </xsl:stylesheet>
Voilà! Alle <code/>-Tags sind entfernt, der Inhalt ist noch da, und alles ohne Suchen&Ersetzen mit regulären Ausdrücken.
Letztes Beispiel: die <code/>-Tags sollen durch <span class="code"/>-Tags ersetzt werden. Dazu werden hier die neuen Elemente einfach als sogenannte literale Elemente (englisch Literal Result Elements) in das Template geschrieben:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xpath-default-namespace="http://www.w3.org/1999/xhtml" exclude-result-prefixes="#default" > <!-- --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- --> <xsl:template match="code"> <span class="code"><xsl:apply-templates/></span> </xsl:template> <!-- --> </xsl:stylesheet>
Man beachte die Verwendung von xpath-default-namespace und exclude-result-prefixes. Dies sorgt dafür, dass das Namespace-Präfix nicht mehr explizit z.B. in das match-Attribut geschrieben werden muss und keine unnötigen Namespace-Angaben in die Ausgabedatei geschrieben werden.
Das letzte Stylesheet gibt es zum Herunterladen, ein passendes Transformations-Szenario für OxygenXML in der Projekt-Datei example.xpr
Tipp: Ich habe die Identity Transformation als Dokumentenvorlage in OxygenXML hinterlegt, weil ich sie sehr oft brauche. Dann sind Mini-Stylesheets sehr schnell geschrieben.
Nachtrag: Die oben angebotene Identity-Transformation hat einige Nebeneffekte: Standard-Attribute aus der DTD werden ergänzt, Zeilenumbrüche werden nicht genau rekonstruiert, Numeric Character References werden in Zeichen umgewandelt. Eine kompliziertere Alternative wird unter http://www.xmlplease.com/identity-template (englisch) vorgestellt.




