XML extensible Markup Language www.w3.org/XML Dr. Beatrice Amrhein 2 Inhaltsverzeichnis 1 XML Grundlagen...........................................................................................................................................5 1.1 Die wichtigsten Punkte.........................................................................................................................5 1.2 XML Sprachen und Anwendungen........................................................................................................7 1.3 Der Aufbau eines XML Dokuments.......................................................................................................7 1.4 Elemente...............................................................................................................................................8 1.5 Attribute.............................................................................................................................................10 1.6 Zeichen- und Entity-Referenzen..........................................................................................................11 1.7 Kommentare und Verarbeitungsanweisungen....................................................................................11 1.8 Die XML Deklaration...........................................................................................................................11 1.9 XML Knotentypen und Nachfolger......................................................................................................12 1.10 Wohlgeformtheit: Zusammenfassung...............................................................................................12 2 Namespaces................................................................................................................................................13 2.1 Wozu dienen Namespaces?................................................................................................................13 2.2 Definition eines Namespaces..............................................................................................................14 2.3 Qualifizierte Element-Namen.............................................................................................................15 3 XSD: XML Schema Definition.......................................................................................................................18 3.1 Deklarationen <--> Definitionen..........................................................................................................20 3.2 Deklaration von einfachen Typen........................................................................................................22 3.3 Vordefinierte primitive Schema Typen................................................................................................23 3.4 Attribut Deklarationen........................................................................................................................25 3.5 Komplexe Schema Typen....................................................................................................................26 3.6 Spezielle Schema Elemente................................................................................................................29 3.7 Schlüssel.............................................................................................................................................32 4 SAX: Simple Application Interface for XML..................................................................................................38 4.1 Was ist SAX?........................................................................................................................................38 4.2 Wofür ist SAX geeignet?......................................................................................................................38 4.3 Wie funktioniert SAX?.........................................................................................................................38 4.4 Übersicht über das SAX API.................................................................................................................39 4.5 Implementation des Default Handlers................................................................................................40 4.6 Fehlerbehandlung: ErrorHandler........................................................................................................42 Ein SAX Beispiel.........................................................................................................................................43 5 DOM: Das Document Object Model............................................................................................................45 Was ist DOM?...........................................................................................................................................45 Die DOM Node Typen...............................................................................................................................45 5.1 Die org.w3c.dom Java Interfaces.........................................................................................................46 5.2 Benutzen des DOM Parsers.................................................................................................................48 6 StAX: Streaming API for XML.......................................................................................................................50 6.1 Was ist StAX?......................................................................................................................................50 7 JAXB: Java Architecture for XML Binding.....................................................................................................54 7.1 Die JAXB Architektur...........................................................................................................................54 7.2 Das Binding.........................................................................................................................................55 7.3 Übersetzung verschiedener Basis-Datentypen....................................................................................56 7.4 Erzeugen der Java Klassen...................................................................................................................57 7.5 Einlesen der XML Daten......................................................................................................................58 7.6 Schreiben der Daten in ein XML File...................................................................................................58 7.7 Anpassen der generierten Java Klassen und Member Variablen.........................................................59 7.8 Externe Binding-Deklaration...............................................................................................................60 7.9 Erzeugen von Schematas....................................................................................................................61 7.10 Schreiben von XML Daten ohne ObjectFactory.................................................................................63 8 Die Transformations-Sprache XSLT..............................................................................................................68 3 8.1 Wie funktioniert XSLT?........................................................................................................................68 8.2 Der Aufbau eines Stylesheets: Templates...........................................................................................70 8.3 Die wichtigsten XSLT Befehle...............................................................................................................72 9 Grundlegendes zu XSLT...............................................................................................................................81 9.1 Default Template Regeln.....................................................................................................................81 9.2 Template Driven vs. Data Driven Stylesheets......................................................................................83 10 XPath: Die XSL Adressierungssprache.......................................................................................................84 10.1 Die XPath Syntax...............................................................................................................................84 10.2 Adressierungs-Achsen.......................................................................................................................88 10.3 Die wichtigsten XPath Funktionen....................................................................................................89 11 XSL-FO Einführung....................................................................................................................................94 11.2 Die wichtigsten FO Elemente............................................................................................................95 11.3 Die wichtigsten Attribute................................................................................................................104 4 1 XML Grundlagen 1.1 Die wichtigsten Punkte XML ist dazu gemacht, Informationen auszutauschen, zu speichern und zu übertragen, aber nicht um Daten darzustellen. In HTML sind Daten und deren Darstellung vermischt. In XML sind die Daten von deren Präsentation getrennt. XML wird benutzt, um Daten zwischen verschiedenen Applikationen, Datenbanken, Computer Systemen und auch über das Internet auszutauschen. • • • • XML ist keine Programmiersprache, sondern ein Standard, um Informationen zu strukturieren, zu verschicken oder zu speichern. XML trennt Information von deren Darstellung -- im Gegensatz zu HTML XML vereinfacht den Daten Austausch zwischen inkompatiblen Systemen. XML vereinfacht den Austausch von Informationen über das Internet (B2B). XML Dokumente sind Unicode Text Files, einfach lesbar und somit einfach benutzbar. • Aus XML entstehen neue Sprachen: SVG, WAP (Wireless Application Protocol ), WML (Wireless Markup Language), CML (Chemical Markup Language ), MathML, ... • Immer mehr Applikationen haben XML Schnittstellen, (Datenbanken, MSExcel, MSWord, SAP...), was den einfachen Austausch von Informationen erlaubt. XML wurde 1998 von einer Arbeitsgruppe des World Wide Web Consortium (W3C) definiert, um Informationen im World Wide Web bereitzustellen. • • extensible: Die Tag-Menge ist erweiterbar (Metasprache) • markup: Die Tags enthalten Metadaten Ein HTML Beispiel <html> <h1>People</h1> <table border="1"> <tr> <th>Name</th> <th>Profession</th> </tr> <tr> <td>Alan Turing</td> <td>computer scientist</td> </tr> <tr> <td>Richard M. Feynman</td> <td>physicist</td> </tr> </table> </html> 5 Ein XML Beispiel XML Dokumente sind reine Datenfiles und enthalten im Gegensatz zu HTML normalerweise keine Präsentations-Hinweise. <?xml version="1.0"?> <!-- file people.xml --> <people> <person> <name>Alan Turing</name> <profession>computer scientist</profession> </person> <person> <name>Richard M. Feynman</name> <profession>physicist</profession> </person> </people> Der Unterschied von XML zu HTML HTML ist endliche Menge von Formatierungselementen wie Überschriften, Absätze, Listen, Tabellen usw. und dient zur Darstellung von Informationen XML ist eine erweiterbare Menge struktureller und inhaltlicher Elemente und enthält die Beschreibung und die Struktur der Informationen 6 1.2 XML Sprachen und Anwendungen Durch Vereinbaren gewisser Tags, wird eine gemeinsame (Austausch-) Sprache für mathematische oder chemische Formeln, Bilder, Ressourcen, ... definiert. • • • • • • • • • • Extensible HyperText Markup Language (XHTML) Scalable Vector Graphics (SVG) Mathematical Markup Language (MathML) XML Schema Definition (XSD) Wireless Markup Language (WML) Chemical Markup Language (CML) Financial Products Markup Language (FpML) User Interface Markup Language (UIML) Geography Markup Language (GML) Bean Markup Language (BML) ... Obwohl XML zunächst vor allem für Internet Applikationen definiert wurde, wird XML heute für verschiedenste Anwendungen eingesetzt. Die Standards werden darum laufend den neuen Bedürfnissen angepasst. • Elektronischer Datenaustausch zwischen Firmen (B2B) • Konfigurationsfiles von Software • • • Erstellen und Verarbeiten von Textdokumenten (vgl. DocBook) Definition von Netzwerk- oder Internet Protokollen (SOAP, WSDL, ...) • Schnittstelle zu Datenbanken Deklarative Programmierung • ... • 1.3 Der Aufbau eines XML Dokuments XML-Dokumente müssen wohlgeformt sein. Das heisst, sie müssen nach gewissen Regeln aufgebaut sein.Ein XML-Dokument heisst wohlgeformt, wenn es die syntaktischen Regeln der Spezifikation einhält, bzw. wenn ein XML-Parser das Dokument ohne Fehler einlesen kann. XML-Dokument XML-Parser Applikation Einige der Regeln sind: • Es gibt im XML-Dokument genau ein äusserstes Element (Root Element). • • Jedes Element hat ein Start- und ein passendes End-Tag. Die Elemente sind korrekt geschachtelt. ... Dieses Kapitel behandelt im Folgenden die vollständigen Regeln für die Wohlgeformtheit. • 7 Ein Beispiel <?xml version="1.0"?> <!-- file people.xml --> <people> <person> <name>Alan Turing</name> <profession> computer scientist </profession> </person> Prolog optionaler Kommentar Root / Document -element <person> <name>Richard M. Feynman</name> <profession> physicist </profession> </person> </people> • <people> ist das Root-Element oder Document-Element des XML Dokuments. Die zwei <person> Elemente sind Kind-Elemente von <people> • Das <name> Element hat als Kind einen Text-Knoten. • 1.4 Elemente Die Struktur eines Elements Start-Tag <person> <name>Alan Turing</name> <profession> computer scientist </profession> </person> Inhalt End-Tag Jedes Element beginnt mit einem Start-Tag (hier <person>) und endet mit einem End-Tag </person>. StartTag und End-Tag müssen identisch sein (case-sensitiv) bis auf den Slash im End-Tag. Gültige Element Namen • • • Elementnamen dürfen Buchstaben, Ziffern, Unterstriche (_), Bindestriche (-) und Punkte (.) enthalten Elementnamen müssen mit einem Buchstaben oder einem Unterstrich beginnen Doppelpunkte sind für Namespaces reserviert Gültige Namen <Drivers_License_Number> <year-month-day> <firstName> Ungültige Namen <Driver's_License_Number> <year/month/day> <first Name> 8 Der Element-Inhalt Der Inhalt eines Elementes kann aus Zeichendaten oder aus weiteren Elementen bestehen: <person> <name> <firstName>Alan</firstName> <lastName>Turing</lastName> </name> <profession>computer scientist</profession> <profession>mathematician</profession> <profession>cryptographer</profession> </person> • • <firstName> und <lastName> sind Elemente, welche als Kinder nur Zeichendaten (Text-Knoten) enthalten. <name> und <person> enthalten weitere Kind-Elemente. Der folgende Baum entspricht (hier) gerade dem DOM (Document Object Model). Die Blätter eines XML-Baums sind entweder Text-Knoten (Zeichendaten, Strings) oder leere Knoten. Man unterscheidet im Baum zwischen Kindern (d.h. direkten Nachfolgern) und allgemeinen Nachfolgern auf beliebigen Stufen (Kinder, Enkel, Urenkel, ...) Elemente mit gemischten Inhalt Textorientierte XML-Dokumente enthalten oft Elemente mit gemischtem Inhalt. Beispiele davon sind Briefe, Reports, Bücher, Artikel, ... <biography> <name>Alan Turing</name> was one of the first people to truly deserve the name <profession>computer scientist</profession> </biography> Das <biography> Element ist ein sog. "mixed element". Es enthält sowohl Zeichendaten (Text-Knoten) als auch Element-Knoten. 9 Im zugehörigen XML Baum hat das biography Kinder darum Elemente und Text-Knoten als Kinder. biography name was one of the ... profession Alan Turing computer scientist Leere Elemente Leere Elemente sind solche ohne Inhalt. <image></image> Bei leeren Elementen darf der End-Tag fehlen, die Darstellung verkürzt sich auf den Start-Tag. <image/> 1.5 Attribute Das Start-Tag eines Elements kann ein oder mehrere Attribute enthalten <person born="1912/06/23" died="1954/06/07"> <name>Alan Turing</name> <image src="turing.jpg"/> </person> • • • • • • • • Nur das Start Tag darf Attribute enthalten. Attribut Werte müssen in Anführungszeichen (") oder in Apostrophe (') eingeschlossen werden Attribut Werte dürfen das Zeichen, in das sie eingeschlossen sind, nicht enthalten Attribut Werte dürfen das Zeichen < nicht enthalten Attribut Werte dürfen das Zeichen & nur am Anfang einer Zeichen- oder Entity-Referenz enthalten Ein Element darf nicht zwei Attribute mit dem gleichen Namen haben Attribute können nicht verschachtelt werden Die Reihenfolge der Attribute ist unwesentlich Attribute sind darum eher für Metadaten, Elemente für die eigentlichen Daten zu verwenden. Die Unterscheidung zwischen Daten und Metadaten ist allerdings nicht immer einfach. 10 1.6 Zeichen- und Entity-Referenzen Sonderzeichen können als Zeichen Referenzen (Entity) mit dem entsprechenden Unicode eingefügt werden: <copyright> Copyright &#xA9; 2001</copyright> Beispiele von Zeichen Referenzen: &#xA9; © (Copyright) &#x20AC; € (Euro) Für die Zeichen &, <, >, ', " gibt es spezielle Entity Referenzen Zeichen Entity-Referenz & &amp; < &lt; > &gt; ' &apos; " &quot; <publisher> O&apos;Reilly &amp; Associates </publisher> Durch Angabe des entsprechenden Zeichensatzes wie zum Beispiel <?xml version="1.0" encoding="ISO-8859-1"?> werden die Umlaute ä, ö, ü als Zeichen erkannt und müssen nicht als Referenzen angegeben werden. 1.7 Kommentare und Verarbeitungsanweisungen Kommentare dienen der Dokumentation des XML Codes und können überall ausser innerhalb eines Tags stehen <!-- These links need to be verified --> Kommentare dürfen ausser am Anfang (<!--) und am Ende (-->) keine "--" Sequenz enthalten. Verarbeitungsanweisungen (Processing Instructions) liefern Informationen für bestimmte Anwendungen und stehen normalerweise im Prolog des XMLDokuments <?robots index="yes" follow="no"?> Eine spezielle Verarbeitungsanweisung ist die XML Deklaration zu Beginn des Dokumentes <?xml version="1.0" encoding="ISO-8859-1"?> Eine andere Verarbeitungsanweisung wird zum Verknüpfen eines XSL-Stylesheets verwendet <?xml-stylesheet href="stl.xsl" type="text/xsl"?> 1.8 Die XML Deklaration Die XML-Deklaration ist optional, muss aber - falls vorhanden - ganz am Anfang des XML-Dokuments stehen <?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="ISO-8859-1"?> Das Encoding gibt die für das XML Dokument verwendete Codierung an. 11 1.9 XML Knotentypen und Nachfolger Wenn man in XML von Knoten redet, meint man die Bestandteile der Dokument-Baumstruktur. Die verschiedenen XML Knotentypen sind • Elemente --> <firstName> ... </firstName> • Attribute --> date="1912" • Textknoten --> Alan • Kommentarknoten --> <!-- Kommentar --> • Verarbeitungsanweisungen --> <? processing introduction ?> Wir betrachten das folgende Beispiel: <people> <!-- Personenbeschreibung--> <person> <name> <firstName>Alan</firstName> <lastName>Turing</lastName> </name> <born date=”1912/06/23”> <profession>computer scientist</profession> <profession>cryptographer</profession> </person> <people> Jedes XML-Dokument beginnt mit einem Wurzelknoten. Dieser hat jedoch keine konkrete Ausprägung, sondern ist nur der abstrakte "Ursprung" der Daten und wird oft mit einem Slash (/) bezeichnet. Erst sein unmittelbarer Abkömmling in der Baumstruktur hat eine konkrete Ausprägung: nämlich das RootElement oder Dokument-Element, also das äusserste, den gesamten Inhalt umfassende Element (hier <people>). Das Root-Element hat in unserem Beispiel zwei Kind-Knoten (bzw. direkte Nachfolger): einen KommentarKnoten und ein person Element. Aus Sicht des person Elements ist der Knoten people der Elternknoten, oder das Elternelement. Der person Knoten hat vier Kind-Knoten, nämlich ein name Element, ein born Elemen und zwei profession Elemente. Das born Element hat einen assoziierten Knoten, nämlich ein Attribut namens date. Der Kind-Knoten von firstName ist der Text-Knoten “Alan”. Die (direkten und indirekten) Nachfolger des person-Elementes sind dessen Kinder (name, born, profession) sowie die indirekten Nachfolger firstName und lastName. 1.10 Wohlgeformtheit: Zusammenfassung XML-Dokumente müssen wohlgeformt sein. Das heisst, sie müssen nach folgenden Regeln aufgebaut sein: • • • • • • • • • Es gibt im XML-Dokument genau ein äusserstes Element (Document- oder Root-Element) Jedes Element hat ein Start- und ein passendes End-Tag (case-sensitive!) Die Elementnamen bestehen aus Buchstaben, Ziffern und den Zeichen Punkt, Unterstrich, Bindestrich und Doppelpunkt (für Namespaces) Elementnamen beginnen mit einem Buchstaben oder einem Unterstrich Die Elemente sind korrekt geschachtelt Alle Attribut-Werte stehen im Start-Tag eines Elements und in Anführungszeichen (Single oder Double Quotes) Ein Element hat nicht mehrere Attribute mit demselben Namen Kommentare und Verarbeitungsanweisungen stehen nicht innerhalb von Tags Attribut-Werte dürfen die Zeichen < nicht enthalten und & nur für Entities 12 2 Namespaces 2.1 Wozu dienen Namespaces? Namespaces werden benötigt um: • Namenskonflikte bei Elementen und Attributen zu vermeiden. • Elemente und Attribute einer bestimmten XML-Anwendung zuzuordnen (XSL, SVG, MathML, FO, ...). Browser können diese automatisch erkennen und umsetzen. • die gewohnten Namen (title, name, type, ...) für verschiedene Anwendungen benutzen zu können. Ein SVG Beispiel <svg:svg xmlns:svg="http://www.w3.org/2000/svg" ... > <svg:g font-family="Helvetica" font-size="6"> <svg:rect x="10" y="33" width="10" height="10" fill="#FFCCCC"/> <svg:text x="7" y="50">0 sec.</svg:text> <svg:rect x="60" y="20" width="10" height="10" fill="#FFCCCC"/> <svg:text x="52" y="40">3 sec.</svg:text> <svg:path d="M-5,0 L0,-10 L5,0 z" fill="blue" stroke="red"> <svg:animateMotion from="15,43" to="65,30" begin="0s" dur="3s"/> </svg:path> </svg:g> </svg:svg> Ein MathML Beispiel MathML wird in diesem Beispiel innerhalb von einer HTML Seite benutzt. <html xmlns="http://www.w3.org/1999/xhtml"> <body> <h4>Can your browser display Presentation MathML?</h4> <p> <m:math xmlns:m="http://www.w3.org/1998/Math/MathML"> <m:mrow> <m:msup> <m:mfenced open="[" close="]"> <m:mrow> <m:mi>a</m:mi> <m:mo>+</m:mo> <m:mi>b</m:mi> </m:mrow> </m:mfenced> <m:mn>260</m:mn> ... </p> ... 13 Beispiel eines Namenskonflikts Namen wie date, title, name, description will man oft in verschiedenstem Kontext benutzen können: <?xml version="1.0"?> <catalog> <description> <title>Impressionist Paintings</title> <creator>Elliotte Rusty Harold</creator> <date>2000-08-22</date> </description> <painting> <title>Memory of the Garden at Etten</title> <artist>Vincent van Gogh</artist> <date> <month>November</month> <year>1888</year> </date> </painting> </catalog> 2.2 Definition eines Namespaces Durch Definieren eines Namespaces werden die Namen eindeutig description creator painting title date artist day month year description dc:creator dc:title dc:date painting pt:artist pt:title pt:day pt:date pt:month pt:year Ein Namespace wird durch einen URI definiert: xmlns:dc=“http://purl.org/dc“ xmlns:m="http://www.w3.org/1998/Math/MathML" • • Der URI zeigt nicht unbedingt auf eine existierende HTML-Seite.Wichtig ist bloss, dass der Namespace eindeutig ist. Parser vergleichen Namespace-URIs zeichenweise. Der Präfix dient als dessen Abkürzung. Für Namespaces werden URL's benutzt, weil diese weltweit eindeutig festgelegt sind. So benutzt zum Beispiel Microsoft für ihre Anwendungen Namespaces der Form http://schemas.microsoft.com/... oder OpenOffice die Namespaces http://openoffice.org/... 14 2.3 Qualifizierte Element-Namen Um Elemente in einen Namespace einzufügen, werden sie mit dem entsprechenden Präfix qualifiziert: <dc:title>Impressionist Paintings</dc:title> Mit einem xmlns-Attribut kann einem Namespace-URI ein Präfix zugeordnet werden: <description xmlns:dc="http://purl.org/dc" > Der Präfix ist nur innerhalb des Elements gültig, in dem er definiert wird. Damit ein Präfix im ganzen Dokument gültig ist, muss er also im Root-Element definiert werden. Die definierten Namespaces werden dann im XML Dokument verwendet, um die Namenskonflikte aufzulösen. <?xml version="1.0"?> <catalog xmlns:dc="http://purl.org/dc" xmlns:pt="http://vangoghgallery.com/pt"> <dc:description > <dc:title>Impressionist Paintings</dc:title> <dc:creator>Elliotte Rusty Harold</dc:creator> <dc:date>2000-08-22</dc:date> </dc:description> <pt:painting> <pt:title>Memory of the Garden at Etten</pt:title> <pt:artist>Vincent van Gogh</pt:artist> <pt:date> <pt:month>November</pt:month> <pt:year>1888</pt:year> </pt:date> </pt:painting> </catalog> Default Namespaces Wird ein Namespace ohne Präfix definiert, so gehören alle Elemente ohne qualifizierten Namen innerhalb dieses Bereichs zu diesem Namespace. <html xmlns="http://www.w3.org/1999/xhtml"> <head><title>Presentation Examples</title></head> <body> <h4>This is a Presentation of MathML</h4> <math xmlns="http://www.w3.org/1998/Math/MathML"> <mrow> <msubsup><mi>x</mi><mn>1</mn><mi>a</mi></msubsup> <mo>+</mo> <msubsup><mi>x</mi><mn>2</mn><mi>b</mi></msubsup> </mrow> </math> </body> </html> 15 16 XSD XML Schema Definition www.w3.org/XML/Schema 17 3 XSD: XML Schema Definition Was ist ein Schema? • • • • XML Schema ist eine XML basierte Alternative für ein DTD. Ein XML Schema beschreibt die Struktur eines XML Dokuments. XML Schema ist eine W3C Recommendation Statt XML Schema wird oft die Abkürzung XSD (XML Schema Definition) benutzt. Ein Schema definiert • • • • • • • • die Elemente, welche im Dokument vorkommen dürfen die Attribute, welche vorkommen dürfen welche Elemente Kind-Elemente sind die Reihenfolge der Elemente die Anzahl der Kind-Elemente ob ein Element leer ist oder Text enthalten kann Datentypen für Elemente und Attribute Default Werte und feste Werte für Elemente und Attribute Die Vorteile von XML Schema XML Schema unterstützt Datentypen • Einfache und exakte Beschreibung der erlaubten Werte • Einfache Verifizierbarkeit der Korrektheit der Daten • Einfaches Definieren von Einschränkungen (facets) an die Daten • Einfaches Definieren von Datenformaten (pattern) • Einfaches Konvertieren der Daten in andere Datentypen • Einfaches Arbeiten mit Daten aus Datenbanken XML Schema benutzt XML Syntax • Keine neue Sprache muss erlernt werden. • Editieren, transformieren und parsen der Schema Files kann durch die selben Editoren oder Tools erfolgen wie für „normale“ XML Dateien. XML Schemas sind erweiterbar • Eigene Datentypen können von den vordefinierten Datentypen hergeleitet werden • Schemas (die darin definierte Typen) können in anderen Schemas wieder verwendet werden • Ein beliebiges XML Dokument kann mehrere Schemas referenzieren. 18 Ein Beispiel Schema <?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.note.org" elementFormDefault="qualified"> <xs:element name="note"> <xs:complexType> <xs:sequence> <xs:element name="to" type="xs:string"/> <xs:element name="from" type="xs:string"/> <xs:element name="heading" type="xs:string"/> <xs:element name="body" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> Das Schema definiert ein neues Vokabular. Alle im Schema definierten Elemente (und ev. Attribute) gehören dann zum Target- Namespace (www.note.org). Ein dazu passendes Instanz Dokument <?xml version="1.0"?> <note xmlns="http://www.note.org" xmlns:xsdi="http://www.w3.org/2001/XMLSchema-instance" xsdi:schemaLocation="http://www.note.org note.xsd"> <to> Bill </to> <from> Jani </from> <heading> Reminder </heading> <body> Don't forget the book! </body> </note> Das XML-Dokument benutzt das im Schema definierte Vokabular. Verknüpfen des Schemas mit dem Instanz Dokument xmlns="http://www.note.org" ist der vom Schema definierte Namespace (hier als Default Namespace gesetzt). xmlns:xsdi="http:// ... /XMLSchema-instance" definiert den xsdi Namespace (für alle xsdi- Befehle). xsdi:schemaLocation= "http://www.note.org note.xsd" gibt das Schema File an: im File note.xsd wird die im Instanz-Dokument benutzte Sprache (zum Namespace http://www.note.org) definiert. 19 Die Teile eines Schemas Eine Element-Deklaration bestimmt den Namen und den Typ eines Elementes. Eine Typ-Definition bestimmt den Namen eines Typs und die Beschreibung (Zusammensetzung) dieses Typs. 3.1 Deklarationen <--> Definitionen Elementdeklarationen sind entweder lokal oder global: Globale Elementdeklarationen sind Kind-Elemente von <schema> und können als Dokumentelement eines Instanz-Dokuments vorkommen. <xs:schema ...> <xs:element name="note"> <xs:complexType> . . . </xs:complexType> </xs:element> . . . </xs:schema> Lokale Elementdeklarationen sind Kind-Elemente einer Typ-Definition und somit nicht global bekannt. <xs:complexType name="addressType"> <xs:sequence> <xs:element name="to" type="xs:string"/> . . . </xs:sequence> . . . Eine Deklaration beschreibt ein Element oder Attribut, welches im Instanz-Dokument vorkommen darf. So wird ein <age>, bzw. ein <product> Element deklariert. <xs:element name="age" type="xs:short"/> <xs:element name="product"> . . . </xs:element> 20 Eine Definition definiert einen Typ (hier productType), welcher dann in einer Element- oder AttributDeklaration verwendet werden kann. <xs:complexType name="productType"> . . . </xs:complexType> Lokale Typen (die in eine Typ-Definition eingebettet sind und also keinen Namen haben) nennt man auch anonyme Typen. Eine Typ-Definition hat immer ein name-Attribut (sonst kann der Typ nirgendwo verwendet werden). Ein solcher Typ nennt man darum benannter Typ. In der Deklaration kann ein Element entweder einen vordefinierten (benannten) Typ verwenden (shoeType) <xs:element name="shoeSize" type="shoeSizeType"/> ... oder einen anonymen Typ definieren <xs:element name="shoeSize"> <xs:complexType> <xs:simpleContent> .... </xs:simpleContent> </xs:complexType> </xs:element> aber nicht beides gleichzeitig! Das Schema Typ-System Einfache Typen sind Strings, Zahlen, Zeittypen oder Listen, bzw. Vereinigungen davon. Elemente mit komplexen Typen können selber wieder Elemente und/oder Attribute enthalten. 21 3.2 Deklaration von einfachen Typen Beispiele von einfachen Elementen mit atomaren Typen <lastName>Schmid</lastName> <age>34</age> <born>1968-03-27</born> Die entsprechenden Deklarationen: <xs:element name="lastName" type="xs:string"/> <xs:element name="age" type="xs:short"/> <xs:element name="born" type="xs:date"/> Elemente mit atomaren (einfachen) Typen können einen Default Wert oder einen festen Wert haben: <xs:element name="color" type="xs:string" default="red"/> <xs:element name="color" type="xs:string" fixed="red"/> Wiederholungen minOccurs/maxOccurs Die Anzahl möglicher Wiederholungen eines Elementes wird mit den Attributen minOccurs und maxOccurs angegeben: <xs:complexType name="familyType"> <xs:sequence> <xs:element name="lastName" type="xs:string"/> <xs:element name="childName" type="xs:string" maxOccurs="10" minOccurs="0"/> </xs:sequence> </xs:complexType> Fehlt das minOccurs oder maxOccurs Attribut, dann gilt automatisch der Defaultwert 1. Die allgemeine Syntax für einfache Elemente: <xs:element name="Elementname" type="Elementtyp" minOccurs="min" maxOccurs="max" default/fixed="Vorgabe" /> 22 3.3 Vordefinierte primitive Schema Typen Es gibt in der Schema Sprache bereits vordefinierte primitive Typen: • Mögliche boolean Werte sind: true, false, 1, 0 • date ist von der Form 2001-03-28 (Jahr-Monat-Tag) • time ist von der Form 21:36:54 (Stunde:Minute:Sekunde) • gYearMonth besteht bloss aus Jahr und Monat • gMonthDay besteht bloss aus Monat und Tag • dateTime ist date und time getrennt durch ein Trenn-Zeichen T (2001-03-28T21:36:54) Abgeleitete String Typen • • • • • • • string: Ein beliebiger String normalizedString: Ein String ohne Tabs oder Newlines token: Ein normalizedString ohne Anfangs-, End- oder aufeinander folgende Spaces anyURI: Ein URI, z.B. http://www.isbe.ch Name: Ein Elementname mit Namespace NCName: Ein Name ohne Namespace language: Ein gültiger xml:lang Wert z.B. DE, EN, FR, ... ID, IDREF, ENTITY, ... sind aus Kompatibilitätsgründen aus der Sprache DTD übernommen. 23 Numerische Datentypen • • • • • • • • • • • • • • • • integer: Eine (beliebig grosse) ganze Zahl nonPositiveInteger: Eine negative Zahl (inklusive 0) negativeInteger: Eine negative Zahl (ohne 0) long: Der Bereich von -9223372036854775808 bis 9223372036854775807 int: Der Bereich von -2147483648 bis 2147483647 short: Der Bereich von 32768 bis 32767 byte: Der Bereich von -128 bis 127 nonNegativeInteger: Von 0 bis unendlich unsignedLong: Von 0 bis 18446744073709551615 unsignedInt: Von 0 bis 4294967295 unsignedShort: Von 0 bis 65536 unsignedByte: Von 0 bis 255 positiveInteger: Von 1 bis unendlich decimal: Eine beliebig lange Zahl der Form 117.08, -98, +24.5, ... float: 32 bit Zahlen der Form 12.56E3, 1.356, INF, -INF, NAN, ... double: 64 bit Zahlen der Form 12.56E3, 12560, 0, INF, -INF, NAN, ... 24 3.4 Attribut Deklarationen Attribute können nicht verschachtelt werden, sie haben also immer einen einfachen Typ. Elemente, welche Attribute enthalten, haben hingegen immer einen komplexen Typ. Ein Beispiel <person gender="male">Peter Muster</person> die Attribut-Deklaration: <xs:attribute name="gender" type="xs:string"/> Wie ein Attribut einem Element zugefügt wird, sehen wir im Kapitel über die Komplexen Typen. Beispiele von Attribut Deklarationen Ein Attribut mit Default Wert: <xs:attribute name="lang" type="xs:string" default="EN"/> Ein Attribut mit fixem Wert: <xs:attribute name="lang" type="xs:string" fixed="EN"/> Ein optionales Attribut: <xs:attribute name="lang" type="xs:string" use="optional"/> Das Attribut muss vorhanden sein: <xs:attribute name="lang" type="xs:string" use="required"/> Vorsicht: Attribute sind optional, ausser sie sind mit use="required" deklariert! List und Union Typen Ein List-Typ definiert eine Liste von einfachen Typen: Beispiel: Liste von ganzen Zahlen <xs:simpleType name="regNumberType"> <xs:list itemType="xs:int"/> </xs:simpleType> Liste von ganzen Zahlen der Länge 4: <xs:simpleType name="shortRegNumberType"> <xs:restriction base="regNumberType"> <xs:length value="4"/> </xs:restriction> </xs:simpleType> Eine Liste von ganzen Zahlen: <regNumber> 4 35 45 213 34 24 12 </regNumber> Eine Liste der Länge 4: <shortRegNumber> 5 13 63 14 </shortRegNumber> Ein Union-Typ ist eine Vereinigung von einfachen Typen: <xs:simpleType name="UnoType"> <xs:union memberTypes="xs:int </xs:simpleType> xs:boolean"/> Elemente vom Typ UnoType wären also zum Beispiel <unoElem>41</unoElem> oder <unoElem>true</unoElem> 25 3.5 Komplexe Schema Typen Ein komplexes Element ist ein XML Element mit einem komplexen Typ. Es gibt vier Arten von komplexen Elementen • leere Elemente (die nur Attribute enthalten), • Elemente, welche nur andere Elemente enthalten, • gemischte Elemente enthalten sowohl andere Elemente als auch Text. • Elemente mit beliebigem Inhalt Elemente mit komplexen Typen können also andere Elemente, Text-Knoten und/oder Attribute enthalten. Leere Elemente Ein leeres Element <product productId="1345" /> Ein mögliches XML Schema: <xs:complexType name="productType"> <xs:attribute name="productId" type="xs:positiveInteger"/> </xs:complexType> <xs:element name="product" type="productType"/> Eine analoge Elementdeklarationen (mit anonymem Typ): <xs:element name="product"> <xs:complexType> <xs:attribute name="prodid" type="xs:positiveInteger"/> </xs:complexType> </xs:element> Ein einfaches Element mit Attribut Zum Beispiel <shoeSize country="france">35</shoeSize> Ein mögliches XML Schema: <xs:complexType name="shoeSizeType" > <xs:simpleContent> <xs:extension base="xs:positiveInteger"> <xs:attribute name="country" type="xs:string"/> </xs:extension> </xs:simpleContent> </xs:complexType> 26 Elemente mit einer Sequenz von Kind-Elementen <person> <firstName>John</firstName> <lastName>Smith</lastName> </person> Ein XML Schema <xs:complexType name="personType"> <xs:sequence> <xs:element name="firstName" type="xs:string"/> <xs:element name="lastName" type="xs:string"/> </xs:sequence> </xs:complexType> Durch Definieren eines benannten Typs kann dieser für Elementdeklarationen wiederverwendet werden <xs:element name="person" type="personType"/> <xs:element name="employee" type="personType"/> Sequenz von Kind-Elementen erweitert um ein Attribut <person gender="male"> <firstName>John</firstName> <lastName>Smith</lastName> </person> Das XML Schema <xs:complexType name="personType"> <xs:sequence> <xs:element name="firstName" type="xs:string"/> <xs:element name="lastName" type="xs:string"/> </xs:sequence> <xs:attribute name="gender" type="xs:string"/> </xs:complexType> Attribute werden immer nach den Kind-Elementen (sequence, choice oder all) aufgeführt. Elemente mit einer unsortierten Folge von Kind-Elementen Der all Indikator gibt an, dass die Kind-Elemente in beliebiger Reihenfolge, aber jedes Kind nur (höchstens) einmal vorkommen darf: <xs:complexType name="personType"> <xs:all> <xs:element name="firstName" type="xs:string"/> <xs:element name="lastName" type="xs:string"/> </xs:all> </xs:complexType> Innerhalb des all Indikators kann minOccurs nur die Werte 0 oder 1 annehmen. maxOccurs kann nur den Wert 1 haben. 27 Elemente mit einer Auswahl von Kind-Elementen Der Choice Indikator bestimmt, dass entweder das eine oder das andere Kind vorkommen soll: <xs:complexType name="personType" > <xs:choice> <xs:element name="employee" type="employeeType"/> <xs:element name="member" type="memberType"/> </xs:choice> </xs:complexType> Eine Person ist also entweder ein employee oder ein member (nicht beides). Elemente mit gemischtem Inhalt Das letter Element ist ein Element mit gemischtem Inhalt. <letter> Dear Mr.<name>J. Smith</name>. Your order <orderid>1032</orderid> will be shipped on <shipdate>2001-07-13 </shipdate>.</letter> Dies wird durch mixed=”true” deklariert: <xs:element name="letter"> <xs:complexType mixed="true"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="orderid" type="xs:positiveInteger"/> <xs:element name="shipdate" type="xs:date"/> </xs:sequence> </xs:complexType> </xs:element> Die Reihenfolge und Anzahl der Kind-Elemente ist dadurch fest vorgegeben. Mixed Elemente werden vor allem für Text-Dokumente und weniger für Daten-Dokumente verwendet. 28 3.6 Spezielle Schema Elemente Wir behandeln in diesem Kurs nur die wichtigsten Schema Elemente. Für eine vollständige Behandlung sämtlicher Schema Elemente (Element- oder Attribut-Gruppen, Substitutionen, Typ-Referenzen, ...) siehe z.B. http://www.w3.org/TR/xmlschema-0. Facetten Einfache Typ-Elemente lassen sich weiter einschränken durch sogenannte Facetten (facets). Mögliche Restriktionen können maximale oder minimale Länge, ein Pattern (reg. Ausdruck), eine Aufzählung der gültigen Werte (enumeration) oder ein minimaler/maximaler Wert sein. Facetten für Strings: length, maxLength, minLength Facetten für numerische Werte: totalDigits, fractionDigits, maxExclusive, minExclusive, maxInclusive, minInclusive Facetten für Zeit-Typen: maxExclusive (<), minExclusive (>), maxInclusive (<=), minInclusive (>=) Für alle Typen: enumeration, pattern, whiteSpace Einige Beispiele Erlaubte Wochentage sind Montag, Mittwoch und Samstag. <xs:simpleType name="weekDayType"> <xs:restriction base="xs:string"> <xs:enumeration value="Montag"/> <xs:enumeration value="Mittwoch"/> <xs:enumeration value="Samstag"/> </xs:restriction> </xs:simpleType> Eine Publisher ID ist entweder von der Form P-ABC oder von der Form P-321: <xs:simpleType name="publisherIdType"> <xs:restriction base="xs:string"> <xs:pattern value="P-[A-Z]+"/> <xs:pattern value="P-[0-9]+"/> <xs:maxLength value="5"/> </xs:restriction> </xs:simpleType> Falls mehrere Pattern angegeben sind, werden diese einfach der Reihe nach probiert. Zuletzt wird hier noch zusätzlich die Länge geprüft. 29 Die Bedeutung der verschiedenen Facetten enumeration Definiert eine Liste von möglichen Werten fractionDigits Spezifiziert die maximal erlaubte Anzahl Dezimalstellen (grösser oder gleich null) length Spezifiziert die (exakte!) Anzahl Buchstaben oder Listenelemente (grösser oder gleich null) maxExclusive Spezifiziert eine obere Schranke für numerische Werte (Werte sind kleiner!) maxInclusive minExclusive Spezifiziert eine obere Schranke für numerische Werte (kleiner oder gleich) Spezifiziert die maximal erlaubte Anzahl Buchstaben oder Listenelemente (grösser oder gleich null) Spezifiziert eine untere Schranke für numerische Werte (Werte sind grösser!) minInclusive Spezifiziert eine untere Schranke für numerische Werte (grösser oder gleich) minLength Spezifiziert die minimale Anzahl Buchstaben oder Listenelemente (grösser oder gleich null) pattern Definiert ein Muster für die erlaubten Werte totalDigits Spezifiziert die maximale Anzahl Ziffern (grösser oder gleich null) whiteSpace Spezifiziert, wie Whitespaces (newlines, tabs, ...) behandelt werden sollen maxLength Beispiele von Facetten Ein letter Wert besteht aus genau einem kleinen Buchstaben (von a bis z) <xs:simpleType name="letterType"> <xs:restriction base="xs:string"> <xs:pattern value="[a-z]"/> </xs:restriction> </xs:simpleType> Ein threeInitials Wert besteht aus genau drei Buchstaben (von A bis Z) <xs:simpleType name="threeInitialsType”> <xs:restriction base="xs:string"> <!--oder [a-zA-Z]{3} --> <xs:pattern value="[a-zA-Z][a-zA-Z][a-zA-Z]"/> </xs:restriction> </xs:simpleType> Oder vier Buchstaben aus a,T und x <xs:restriction base="xs:string"> <xs:pattern value="[aTx] {4} "/> </xs:restriction> Eine product Id besteht aus 4 Ziffern <xs:simpleType name="productIdType"> <xs:restriction base="xs:int"> <xs:pattern value="[0-9][0-9][0-9][0-9]"/> </xs:restriction> </xs:simpleType> <!--oder [0-9]{4} --> 30 myString besteht aus 5 bis acht kleinen Buchstaben und Zahlen <xs:simpleType name="myStringType"> <xs:restriction base="xs:string"> <xs:pattern value="([a-z0-9])*"/> <xs:minLength value="5"/> <xs:maxLength value="8"/> </xs:restriction> </xs:simpleType> Oder einem Grossbuchstaben gefolgt von mindestens einem kleinen Buchstaben <xs:restriction base="xs:string"> <xs:pattern value="[A-Z]([a-z])+"/> </xs:restriction> Der XML-Parser soll die Leer-Zeichen mitzählen <xs:simpleType name="addressType"> <xs:restriction base="xs:string"> <xs:minLength value="5"/> <xs:whiteSpace value="preserve"/> </xs:restriction> </xs:simpleType> Nur Werte von 0 bis 100 <xs:simpleType name="ageType"> <xs:restriction base="xs:int"> <xs:minInclusive value="0"/> <xs:maxInclusive value="100"/> </xs:restriction> </xs:simpleType> oder ohne "Rand" (1 bis 99 ) <xs:restriction base="xs:int"> <xs:minExclusive value="0"/> <xs:maxExclusive value="100"/> </xs:restriction> Die wichtigsten regulären Ausdrücke Bedeutung \ . x ^x [x] () | {x} {x,} {x,y} ? * + \d \D \s \S Escape, um Instanzen von Zeichen zu finden, welche als Metazeichen benutzt werden (wie Punkt, Klammer, ... ) Ein beliebiges Zeichen (ausser newline) Eine Instanz von x Jedes Zeichen ausser x Alle Zeichen in diesem Bereich ( z.Bsp. [abuv] die Buchstaben a, b, u oder v, [a-z] alle Kleinbuchstaben) Runde Klammern dienen für die Gruppierung Der OR Operator (Auswahl) (a|A) Der Ausdruck muss genau x Mal vorkommen Der Ausdruck muss mindestens x Mal vorkommen Der Ausdruck kommt mindestens x Mal und höchstens y Mal vor. Abkürzung für {0,1} Abkürzung für {0, } Abkürzung für {1, } \d : Abkürzung für [0-9] (digits) \D : alles ausser Ziffern \s : Whitespace (spaces, tabs, newlines, ...) \S : kein Whitespace 31 3.7 Assertions (Zusicherungen) Mit Hilfe von Zusicherungen können weitere Einschränkungen an Werte von Elementen oder Attributen definiert werden. Werte von Elementen oder Attributen können Verglichen oder in Beziehung gesetzt werden. Es können auch komplexe Bedingungen an Werte definiert werden. Beispiele mit xs:assert Die Namen (Vorname und Nachname) sollen verschieden sein. <xs:complexType name="nameType"> <xs:sequence> <xs:element name="first_name" type="xs:string"/> <xs:element name="last_name" type="xs:string"/> </xs:sequence> <xs:assert test="first_name != last_name"/> </xs:complexType> Das Start-Datum soll nach dem End-Datum liegen (soll grösser sein). <xs:complexType name="pType"> <xs:sequence> <xs:element name="name" type="nameType"/> </xs:sequence> <xs:attribute name="startDate" type="xs:date"/> <xs:attribute name="endDate" type="xs:date"/> <xs:assert test="@startDate > @endDate"/> </xs:complexType> Die Anzahl Eigenschaften (profession und hobby) sollen dem Wert des properties Attributs entsprechen. <xs:complexType name="personType"> <xs:sequence> <xs:element name="name" type="nameType"/> <xs:element name="profession" type="xs:string" maxOccurs="9"/> <xs:element name="hobby" type="xs:string" maxOccurs="9"/> </xs:sequence> <xs:attribute name="properties" type="xs:int"/> <xs:assert test="@properties eq count(profession | hobby)"/> </xs:complexType> Ein gültiges Instanz-Dokument <person properties="2"> <name> <first_name>Richard</first_name> <middle_name>P.</middle_name> <last_name>Feynman</last_name> </name> <born addressRef="unq" date="1918"/> <died date="1988"/> <profession>physicist</profession> <hobby>African drum music</hobby> </person> 32 Beispiele mit xs:assertion Für komplexe (inhaltliche) Einschränkungen an einfache Datentypen gibt es das xs:assertion Element. Der Wert soll kleiner als 1800 oder grösser als 1900 sein, aber ungleich null. <xs:simpleType name="spezType"> <xs:restriction base="xs:int"> <xs:assertion test="($value &lt; 1800 or $value > 1900) and $value != 0"/> </xs:restriction> </xs:simpleType> Der (integer) Wert soll ungerade sein. <xs:simpleType name="oddType"> <xs:restriction base="xs:int"> <xs:assertion test="($value mod 2 != 0)"/> </xs:restriction> </xs:simpleType> 33 3.8 Schlüssel Mit Hilfe von keys können Elemente oder Attribute als Schlüssel definiert und damit die Eindeutigkeit innerhalb eines Instanzdokuments garantiert werden <xs:key name="departmentId"> <xs:selector xpath="department"/> <xs:field xpath="@id"/> </xs:key> Die Schlüsseldefinition befindet sich normalerweise in der Deklaration des Root-Elements des XMLDokuments. Der xs:selector enthält die Adresse, wo sich das Schlüsselfeld befindet (hier im department Element direkt unter dem Root Element). Ein oder mehrere xs:field Elemente definieren die Schlüsselfelder (hier ein Attribut namens id). Ein analoges Konstrukt ist die unique Definition. Auch damit kann die Eindeutigkeit von Elementen festgelegt werden. <xs:unique name="nameUnique"> <xs:selector xpath="name"/> <xs:field xpath="@id"/> </xs:unique> Referenzen auf Schlüssel Mittels keyref können Referenzen auf Schlüssel (key) definiert werden <xs:keyref name="departmentRef" refer="departmentId"> <xs:selector xpath="employee/department"/> <xs:field xpath="@ref"/> </xs:keyref> Die Schlüssel-Referenz-Definition befindet sich normalerweise direkt bei der Schlüsseldefinition. Der xs:selector enthält die Adresse, wo sich die Schlüsselreferenz befindet (hier im employee/department Element unter dem Root Element). Das xs:field definiert die Felder der Schlüsselreferenzen (hier ein Attribut namens ref). Ein Beispiel Schema mit Schlüsseldefinition In den Elementen person/born und person/died gibt es je eine Referenz auf eine Adresse. <?xml version="1.0" encoding="UTF-8"?> <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="people"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="person" type="personType"/> <xs:element maxOccurs="unbounded" name="address" type="addressType"/> </xs:sequence> </xs:complexType> <xs:key name="addressID"> <xs:selector xpath="address"/> <xs:field xpath="@id"/> </xs:key> <-- alle Adress-Ids sind verschieden --> 34 <xs:keyref name="bornRef" refer="addressID"> <xs:selector xpath="person/born"/> <xs:field xpath="@addressRef"/> </xs:keyref> <-- addressRef bezieht sich auf eine Adress-Id --> <xs:keyref name="diedRef" refer="addressID"> <xs:selector xpath="person/died"/> <xs:field xpath="@addressRef"/> </xs:keyref> </xs:element> <xs:complexType name="personType"> . . . </xs:complexType> <xs:complexType name="addressType"> <xs:sequence> <xs:element name="country" type="xs:string"/> <xs:element name="city" type="xs:string"/> </xs:sequence> <xs:attribute name="id" type="xs:string"/> </xs:complexType> <xs:complexType name="bdType"> <xs:attribute name="addressRef" type="xs:string"/> <xs:attribute name="date" type="xs:positiveInteger"/> </xs:complexType> </xs:schema> Ein mögliches Instanzdokument <?xml version="1.0" encoding="UTF-8"?> <people xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="People.xsd"> <person> <name> <firstName>Alan</firstName> <lastName>Turing</lastName> </name> <born date="1912" addressRef="gbl"/> <!-- Referenz auf eine Adresse--> <died date="1954" addressRef="gbc"/> <!-- Referenz auf eine Adresse--> <profession>computer scientist</profession> <profession>mathematician</profession> <profession>cryptographer</profession> <hobby>philosophy</hobby> <hobby>biology</hobby> </person> <address id="gbl"> <!-- Schlüssel --> <country>Great Britain</country> <city>London</city> </address> <address id="gbc"> <!-- Schlüssel --> <country>Great Britain</country> <city>Cambridge</city> </address> </people> 35 Any-Elemente und Attribute Ein <any> Element erlaubt uns, das XML Dokument zu einem späteren Zeitpunkt um beliebige Elemente zu erweitern. Das selbe bewirkt ein <anyAttribute> für Attribute. <xs:element name="person"> <xs:complexType> <xs:sequence> <xs:element name="firstName" type="xs:string"/> <xs:element name="lastName" type="xs:string"/> <xs:any minOccurs="0"/> </xs:sequence> <xs:anyAttribute/> </xs:complexType> </xs:element> Eine mögliche Erweiterung, zum Beispiel um ein Children Element und ein gender Attribut: <xs:element name="children"> <xs:complexType> <xs:sequence> <xs:element name="childName" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:attribute name="gender"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern value="male | female"/> </xs:restriction> </xs:simpleType> </xs:attribute> Ein gültiges (erweitertes) Instanz-Dokument Das person Element kann so um ein gender Attribut und ein children Element erweitert werden. <person gender="female"> <firstName>Maria</firstName> <lastName>Stuard</lastName> <children> <childName>Jakob</childName> <childName>Cecilia</childName> </children> </person> 36 Schema Dokumentation Anmerkungen (xs:annotation) enthalten Informationen für den Benutzer (xs:documentation) oder für eine Applikation (xs:appinfo) <xs:annotation> <xs:documentation xml:lang="en"> This is a schema for address books </xs:documentation> </xs:annotation> Einfügen von Schemas Mittels include kann ein anderes Schema eingefügt werden, welches denselben Target-Namespace hat: <xs:include schemaLocation="address.xsd"/> Mittels import kann ein Schema eingefügt werden, welches einen andern Target-Namespace hat: <xs:import namespace="http://www.example.org/address" schemaLocation="address.xsd"/> Include erweitert also das Schema um weitere Typen desselben Namespaces. Import hingegen erlaubt das Benutzen von Typen aus anderen Namespaces. Typen überschreiben: xs:override Ab Schema Version 1.1 können (einzelne) Schema Typen in einem anderen Schema überschrieben und dadurch neu definiert werden. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"# xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"> <xs:override schemaLocation="OverrideBase.xsd"> <xs:simpleType name="DressSizeType"> <xs:restriction base="xs:integer"> <xs:minInclusive value="2"/> <xs:maxInclusive value="16"/> </xs:restriction> </xs:simpleType> </xs:override> <xs:element name="newSize" type="DressSizeType"/> </xs:schema> 37 38 Verarbeiten von XML Dokumenten SAX: Simple Application Interface for XML org.xml.sax DOM: Document Object Model www.w3.org/DOM StAX: Streaming API for XML javax.xml.stream 39 4 SAX: Simple Application Interface for XML 4.1 Was ist SAX? SAX definiert ein einfaches API für einen Event basierten Parser. Event basiert heisst, der Parser liest ein XML Dokument von Anfang bis Ende und benachrichtigt bei jeder erkannten syntaktischen Struktur (Element, Prozess Instruktion, Kommentar, Namespace-Definition, ...) die Applikation. Die Benachrichtigung erfolgt durch Aufruf der entsprechenden Funktionen • startDocument • startElement • endElement • endDocument • .... SAX funktioniert wie ein serieller I/O Stream In SAX ist kein Random Access möglich. Der Parser läuft linear durch das Dokument, zurückgehen auf eine frühere Position ist nicht möglich. SAX ist darum vor allem dann nützlich, wenn wir die Daten des XML Dokuments lesen oder gewisse Elemente heraus filtern (aber das Dokument nicht verändern oder bearbeiten) wollen. 4.2 Wofür ist SAX geeignet? Sax ist geeignet • für das schnelle, effiziente Lesen oder Validieren von XML Dokumenten. • für grosse Dokumente wenn wenig Speicher zur Verfügung steht. SAX benötigt wenig Speicher, da die gelesenen Daten nicht intern abgespeichert, sondern direkt an die Applikation weitergeleitet werden. • falls keine Änderungen in der XML Struktur nötig sind. Wenn die XML Struktur oder die XML Daten verändert werden sollen oder die Daten nicht linear durchlaufen werden sollen, ist SAX nicht geeignet. Für solche Anwendungen sind XSLT, StAX oder DOM geeigneter. 4.3 Wie funktioniert SAX? Ganz zu Beginn beim Lesen wird ein startDocument Event erzeugt. Danach werden abhängig von der Struktur des XML Dokuments die folgenden Events erzeugt. <name> <first_name> Alan </first_name> <last_name> Turing </last_name> </name> --> --> --> --> --> --> --> --> startElement() startElement() characters() endElement() startElement() characters() endElement() endElement() Eine Start-Tag (<...>) triggert ein startElement Event, ein End-Tag (</...> ) triggert ein endElement Event. Text Knoten triggern ein characters Event. Als letztes wird ein endDocument Event erzeugt. 40 4.4 Übersicht über das SAX API XMLParserFactory Erzeugen eines SaxParsers SAX Parser Setzen der Parser Eigenschaften (validating, namespace-aware, ...) Bietet verschiedene parse() Methoden an (aus File, InputStream, URI, ...). Implementiert die verschiedenen Handler (Interfaces) leer. Interface für die verschiedenen Event Handler Methoden start / endDocument, start / endElement(), characters, ... DefaultHandler ContentHandler ErrorHandler Interface für die Fehlerbehandlung: error(), fatalError(), warning() DTDHandler Implementation der DTD Anweisungen (wird normalerweise nicht mehr gebraucht) Funktionsweise des SAX Parsers 41 4.5 Implementation des Default Handlers Um einen Sax-Parser zu schreiben, überschreibt man üblicherweise einfach die Klasse Default Handler. Da dort bereits alle vom Interface geforderten Methoden leer implementiert sind, müssen wir nur noch die von uns benötigten Methoden überschreiben. startDocument / endDocument Beim Parsen des XML Dokuments wird als erstes die Methode startDocument() aufgerufen. In dieser können alle Initialisierungs-Schritte ausgeführt werden. Ganz am Ende wird dann die Methode endDocument() aufgerufen, die zum Freigeben und Aufräumen aller benutzten Ressourcen verwendet werden kann. startElement public void startElement( String uri, // String lName, // String qName, // Attributes attrs) // throws SAXException Namespace URI lokaler Name qualifizierter Name Attribut-Liste Die Methode startElement() wird immer dann aufgerufen, wenn der Parser ein Start-Tag liest. Den Tag-Namen finden wir im Parameter qName (Tag-Name mit Namespace-Prefix, falls vorhanden), oder als lName (lokaler Name) und uri (Namespace). uri und lName können leere Strings sein, falls der Parser den Namespace ignoriert (durch Setzen der entsprechenden namespace-Property). Im attrs-Argument befindet sich die Liste aller Attribute. Falls keine Attribute vorhanden sind, ist attrs eine leere Attribut-Liste. In startElement können wir also die Attribute (name und value) des Elements lesen: if (attrs != null) { for(int i = 0; i < attrs.getLength(); i++) { // Attribute name (local or qualified) String aName = attrs.getLocalName(i); if ("".equals(aName)) aName = attrs.getQName(i); // Attribute value String val = attrs.getValue(i); } } Die Attribute haben entweder einen lokalen Namen String getLocalName(int i) oder einen qualifizierten Namen (mit Namespace) String getQName(int i) Den Wert des Attributes erhalten wir entweder über den Listen-Index String getValue(int i) oder über den Namen String getValue(String aName). 42 characters Der Parser ruft für jeden Textknoten (einmal oder mehrmals) die Methode characters auf. StringBuffer textBuffer = new StringBuffer(); public void characters( char chars[], // die Zeichen aus dem XML Dokument int start, // die Start-Position im Array int length ) // die Anzahl Zeichen, welche gelesen werden können throws SAXException { // Auslesen der Zeichen aus dem Puffer chars und // abspeichern in den Text-Puffer textBuffer.append(chars, start, length); } Ein SAX Parser kann alle Zeichen des Dokuments auf einmal zurückgeben, oder sie in verschiedene Blöcke zerteilen. Das Argument start gibt an, wo im Puffer chars die für diesen Textknoten relevante Information anfängt. length gibt an, wie viele Zeichen des Puffers (ab Position start) zu diesem Aufruf gehören. endElement public void endElement( String uri, // Namespace URI String lName, // lokaler Name String qName ) // qualifizierter Name throws SAXException { } // verarbeite den Inhalt des Elements // ev. aufräumen. Die endElement Methode wird bei jedem XML End-Tag (oder am Ende eines leeren Elements) aufgerufen. Da der Text–Inhalt eines Elements oft in mehreren Blöcken eingelesen wird, ist es sinnvoll, den Text-Puffer aus der characters Methode erst in der endElement Methode zu verarbeiten. ProcessingInstruction Die Methode processingInstruction dient zur Behandlung von Verarbeitungs-Anweisungen. Verarbeitungs-Anweisungen werden normalerweise vom Parser ignoriert. Die Methode processingInstruction kann zum Lesen der Verarbeitungsanweisungen benutzt werden. startPrefixMapping / endPrefixMapping Diese Methoden dienen zum Behandeln von Namespaces am Anfang, bzw. am Ende des Setzen eines Namespace Prefix. start/endPrefixMapping wird immer am Anfang, bzw. am Ende des Gültigkeitsbereiches einer Namespace Deklaration aufgerufen. 43 4.6 Fehlerbehandlung: ErrorHandler Warnungen werden während der Verarbeitung des DTD oder des Schemas erzeugt: Unnötige (doppelte) Deklarationen für Entities, Attribute oder Notations oder Fehler im Schema können zum Aufruf der Methode warning() führen. Ein Fehler (Aufruf von error()) entsteht, falls das XML-Dokument nicht dem Schema oder DTD entspricht, also das XML-Dokument nicht gültig ist. Die Methode fatalError() wird aufgerufen, falls das XML-Dokument nicht wohlgeformt ist. Bei einem fatalError bricht der Parser normalerweise ab. Um diese Fehler erkennen und bearbeiten zu können, ist es wichtig den ErrorHandler sinnvoll zu implementieren und Fehlermeldungen auszugeben. Die SAXParseException kennt die Zeile/Spalte, wo der Fehler im Dokument aufgetreten ist: e.getLineNumber() --> Ausgabe der Zeilen Nummer e.getColumnNumber() --> Ausgabe der Spalten Nummer e.getLocalizedMessage() --> Ausgabe der Fehlermeldung Ein SAX Beispiel Ein vollständiges SAX Beispiel finden Sie auf der Übungsseite. 44 5 DOM: Das Document Object Model Was ist DOM? • • • • • DOM definiert einen Standard zum Lesen und Bearbeiten von XML-Dokumenten. DOM stellt XML-Dokumente als Baumstruktur dar. DOM definiert eine Menge von Interfaces (Node, NodeList, Element, Attr, ...) und eine Menge von Methoden darauf. DOM ist Plattform- und Sprachunabhängig definiert. DOM ist ein W3C Standard. Die DOM Node Typen Der W3C Standard definiert die folgenden Knoten-Typen: Node nodeName() nodeValue() attributes NodeType() Attr name of attribute value of attribute null 2 CDATASection #cdata-section content of the CDATA Section null 4 Comment #comment content of the comment null 8 Document #document null null 9 DocumentFragment #document-fragment null null 11 DocumentType document type name null null 10 Element tag name null NamedNode Map 1 Entity entity name null null 6 EntityReference name of entity referenced null null 5 Notation notation name null null 12 ProcessingInstruction target entire content excluding null the target 7 Text content of the text node null 3 #text 45 5.1 Die org.w3c.dom Java Interfaces Für jeden Knoten-Typ definiert Java ein entsprechendes Interface im org.w3c.dom Package. • Element für Elementknoten • Attr für Attribute • Text für Text Knoten • Document für das Document Element • Node für beliebige Knoten • NodeList für Listen von Knoten • ProcessingInstruction für Verarbeitungs-Anweisungen • ... Das Node Interface Das Node Interface definiert die Methoden zum Lesen und Bearbeiten von DOM Knoten: • Node appendChild(Node newChild) fügt einen neuen Knoten am Ende der Liste der Kind-Knoten ein. • Node cloneNode(boolean deep) Kopiert diesen Knoten (deep=true --> mit allen Kind-Knoten) • NamedNodeMap getAttributes() gibt eine NamedNodeMap zurück, welche die Attribute dieses Elements enthält oder null, falls der Knoten kein Elementknoten ist. • NodeList getChildNodes() gibt eine NodeList mit allen Kindknoten zurück. • Node getFirstChild() / getLastChild() gibt den ersten/letzten Kind-Knoten dieses Knotens zurück. • String getLocalName() gibt den lokalen Namen (ohne Namespace Prefix) zurück. • short getNodeType() / String getNodeValue() der Node Type / Node Value (gemäss Tabelle in Abschnitt 8.2) • Node getParentNode() gibt den Elternknoten dieses Knotens zurück (falls er existiert). • Node removeChild(Node oldChild) entfernt oldChild aus der Liste der Kinder dieses Knotens. • ... Das NodeList Interface Das NodeList Interface definiert Methoden zum Bearbeiten von (geordneten) Knotenmengen. Eine NodeList erhalten wir zum Beispiel als Rückgabewert von getChildNodes. Das NodeList Interface definiert die beiden Methoden • int getLength() gibt die die Länge der Liste zurück. • Node item(int n ) gibt das n-te Element der Liste (beginnend bei 0) zurück. Das Document Interface Die wichtigsten Methoden des Document Interfaces sind 46 • NodeList getElementsByTagName(String tagname) NodeList getElementsByTagNameNS(String namespace, String localName) gibt eine NodeList mit allen Elementen mit dem Namen tagname zurück. Die Reihenfolge der Liste ist die gleiche wie im XML-Dokument. Diese Methode ist im allgemeinen einfacher zu benutzen als die Methode getChildNodes, da hier alle zurückgegebenen Knoten Elementknoten sind. • void normalizeDocument() hat den gleichen Effekt, wie wenn das Dokument gespeichert und neu geladen würde. Das bedeutet, dass (beim validierenden DOM) erneut die Gültigkeit des Dokuments geprüft wird. So kann geprüft werden, ob ein Dom-Baum im Memory immer noch der im Schema vorgegebenen Struktur entspricht. • Element getElementById(String id) gibt das eindeutige Element mit der ID id zurück (falls das Element existiert und die id als Id deklariert ist). Mit Hilfe dieser Methoden können Referenzen aufgelöst werden. Das Element Interface Die wichtigsten Methoden des Element Interfaces sind • String getAttribute(String name) String getAttributeNS(String namespace, String localName) gibt den Inhalt des entsprechenden Attributs zurück (ohne / mit Namespace). • void removeAttribute(String name) removeAttributeNS(String namespace, String localName) Löscht im Element das Attribute name (ohne / mit Namespace) • void setAttribute(String name, String value) / void setAttributeNS(String ns, String name, String value) Fügt in das Element ein neues Attribut ein. • NodeList getElementsByTagName(String tagname) NodeList getElementsByTagNameNS(String namespace, String localName) gibt alle Nachfolger dieses Elements mit Namen tagname zurück (ohne/mit Namespace). • void setIdAttribute(String name, boolean isId) void setIdAttributeNS(String ns, String localName, boolean idId) Falls isId gleich true ist, wird das entsprechende name Attribute zu einem ID-Attribut (es gibt keine Methode setIdElement). Diese Methode wird vorallem im Zusammenhang mit getElementById() gebraucht. 47 5.2 Benutzen des DOM Parsers public static final String fileName = "XML/catalog.xml"; private static final String schemaFile = "XML/catalog.xsd"; private static final String namespaces = "http://xml.org/sax/features/namespaces"; static public void main(String[] argv) { try { // create validating document builder factory DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature(namespaces, true); // use validating parser SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = schemaFactory.newSchema(new StreamSource(new File(schemaFile))); factory.setSchema(schema); // parse XML tree into DOM document tree DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(new MyErrorHandler()); Document document = builder.parse(fileName); . . . } catch (Exceptions e) { . . . } } Navigieren durch den DOM Baum Das Document Interface bietet Methoden zum Sammeln aller Elemente mit gleichem Tag Name. DieseElemente können dann durch Iteration durch die NodeList gelesen werden: NodeList nodeList = document.getElementsByTagName("tagName"); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); String value = node.getNodeValue(); // Wert des i-ten Elements } NamedNodeMap attrs = node.getAttributes(); // Wert des id Attributs String idVal = attrs.getNamedItem("id").getNodeValue(); . . . Ausserdem können wir das Element mit einer bestimmten ID suchen: Element aNode = document.getElementById(idref); getElementById() funktioniert aber nur , falls isId(id) zu true evaluiert. Dazu muss id entweder im Schema oder programmatisch als ID definiert sein. Das kann zum Beispiel erreicht durch das explizite Setzen dieser Eigenschaft: z. B. das Attribute mit Namen „id“ eines nodes wird durch node.setIdAttribute(“id“, true); zu einer ID. 48 Falls ein Knoten kopiert werden soll, muss er zuerst durch clone() verdoppelt werden. Das boolesche Argument (true/false) gibt an, ob der ganze Unterbaum neu angelegt werden soll. Node clone = aNode.cloneNode(true); Node parent = node.getParentNode(); parent.appendChild(clone); // kopiere den Knoten aNode // einfügen in parent von node Löschen von Nodes Da beim Löschen von Elementen aus einer NodeList gleichzeitig die Liste verändert wird, sollten die ListenElemente rückwärts (vom Ende zum Anfang der Liste) oder in einer while-Schleife gelöscht werden: NodeList nodeList = document.getElementsByTagName("tagName"); for (int i = nodeList.getLength()-1; i >= 0; i--) { // Rückwärts! Node node= nodeList.item(i); Node parent = node.getParentNode(); parent.removeChild(node); . . . Ausgeben eines DOM Baums: LSSerializer Seit dem DOM Level 3 gibt es mit Hilfe des LSSerializers eine komfortable Möglichkeit zum Schreiben von DOM Dokumenten. Ein Beispiel dazu: import com.sun.org.apache.xml.internal.serialize.*; OutputFormat format = new OutputFormat(DOM_Document); format.setLineWidth(lineWidth); // define line width format.setIndenting(true/false); // define indentation format.setIndent(indent); // number of spaces format.setOmitDocumentType(true/false); // omit Document Type definition FileOutputStream fos = new FileOutputStream(resultFile); XMLSerializer serializer = new XMLSerializer(fos, format); serializer.serialize(DOM_Document); // print out document Ein vollständiges DOM Beispiel finden Sie auf der Übungsseite. 49 6 StAX: Streaming API for XML 6.1 Was ist StAX? StAX ist ein JAVA API, um XML Dateien zu verarbeiten. Die beiden vorherigen XML-APIs sind entweder • baumbasiert – das gesamte Dokument wird als Baumstruktur in den Speicher gelesen und kann dann dort bearbeitet werden (DOM), oder • ereignisbasiert – die Anwendung bekommt ein Ereignis, wenn neue Knoten im Dokument entdeckt werden (SAX). Beide Varianten haben Vor- und Nachteile: die erste erlaubt wahlfreien Zugriff auf das Dokument, die zweite benötigt weniger Speicher und ist daher üblicherweise viel schneller. Eine baumbasierte API erlaubt einen lesenden und schreibenden Zugriff auf das Dokument, eine ereignisbasierte API ist ein einmaliger lesender Durchlauf durch das Dokument. StAX wurde als komfortablere Alternative für SAX entworfen. In StAX ist der programmatische Einstieg ein Cursor, der eine Stelle im Dokument repräsentiert. Die StAX Anwendung bewegt den Cursor selber vorwärts. Die StAX API Dokumentation finden Sie unter javax.xml.stream. Wir beschränken uns hier auf die beiden Interfaces XMLStreamReader und XMLStreamWriter. XML-Dokumente lesen Um mit Hilfe von StAX eine XML-Datei verarbeiten zu können, muss zuerst ein XMLStreamReader erzeugt werden. Der XMLStreamReader dient dann als Parser zum Lesen des Dokuments. InputStream inputStream = new FileInputStream(inFile); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createXMLStreamReader(inputStream); Der Reader liefert beim Lesen des XML-Dokuments eine Reihe von Events. Die verschiedenen Event-Typen sind als Konstanten in der Klasse XMLStreamConstants festgelegt: • • • • • START_DOCUMENT: END_DOCUMENT --> Beginn / Ende des XML-Dokuments. START_ELEMENT: Start Tag des Elements --> Die Attribute können hier gelesen werden. END_ELEMENT --> End Tag des Elements. CHARACTERS --> gesamter Textknoten innerhalb eines Elements. ... Ein Beispiel: Lesen eines XML-Dokuments mit StAX while (reader.hasNext()) { switch (reader.next()) { case XMLStreamConstants.START_ELEMENT: System.out.print(reader.getName()); if (reader .getAttributeCount() > 0) System.out.print(" : " + reader.getAttributeValue(0)); break; case XMLStreamConstants.END_ELEMENT: System.out.print(reader.getName()); break; case XMLStreamConstants.CHARACTERS: System.out.print(reader.getText()); break; } 50 Die Benutzung von StAX ist viel intuitiver als diejenige von SAX, so liest die Methode getText() den ganzen Text-Knoten und nicht nur den nächsten Block. XML-Dokumente schreiben Im Gegensatz zu DOM-orientierten APIs, bei denen das gesamte Dokument im Speicher vorliegt und verändert werden kann, ist es bei StAX nicht möglich, die vorhandene XML-Datei zu verändern. Mit Hilfe von SAX ist es sehr mühsam, ein XML-Dokument zu schreiben. Hier bietet STaX hingegen gute Möglichkeiten. Mit Hilfe der XMLOutputFactory wird ein XMLStreamWriter erzeugt: FileOutputStream fos = new FileOutputStream(outFile); XMLOutputFactory factory = XMLOutputFactory.newInstance(); XMLStreamWriter outWriter =factory.createXMLStreamWriter(fos); Der XMLStreamWriter bietet dann Methoden zum Schreiben der XML-Elemente und Attribute: outWriter.writeStartDocument("1.0"); outWriter.writeStartElement(name); outWriter.writeAttribute(attr, value) outWriter.writeCharacters(string) outWriter.writeComment(comment) outWriter.writeEndElement(); --> --> --> --> --> --> <?xml version="1.0"?> <name> <... attr=“value“> string <!-- comment --> </name> (aus dem Kontext) Es muss nicht angegeben werden, welches Element geschlossen werden muss. Dies erkennt StAX mit Hilfe der Wohlgeformtheit jeweils von selber. Ein vollständiges StAX Beispiel finden Sie auf der Übungsseite. 51 52 JAXB Java Architecture for XML Binding jaxb.java.net 53 7 JAXB: Java Architecture for XML Binding JAXB ist eine schnelle und einfache Methode, um ein XML Schema mit einer Java Repräsentation zu verbinden. Damit ist es sehr einfach, XML Daten nach Java einzulesen, dort zu verarbeiten und wieder nach XML abzuspeichern. JAXB erzeugt aus dem Schema automatisch eine entsprechende Java Daten Hierarchie, liest die Daten aus dem Dokument und stellt Zugriffs-Methoden für alle Elemente zur Verfügung. JAXB kann als Ersatz für DOM oder SAX benutzt werden. Die Programmier-Details für das Lesen und Schreiben der XML Daten bleiben verborgen. Wir müssen keine neuen Befehle kennen lernen. Der Zugriff auf die Daten erfolgt nach den Namen, wie sie im XML Schema definiert sind. 7.1 Die JAXB Architektur Eine JAXB Implementation besteht aus den folgenden Teilen: • Der Schema Compiler Erzeugt aus einem Schema eine Menge von dazu passenden Java Klassen. Das Mapping erfolgt gemäss der Namen im Schema, kann aber durch Schema-Annotations oder ein BindingsDeklarationsfile angepasst werden. • Der Schema Generator Erzeugt aus existierenden Java Klassen ein passendes Schema. Das Mapping erfolgt gemäss den Annotations in den Java Klassen. • Das Binding Runtime Framework Bietet die Operationen unmarshalling (reading) und marshalling (writing) um von Java aus auf XML Daten zuzugreifen, sie zu ändern oder zu validieren. 54 7.2 Das Binding Der Binding Prozess besteht aus den folgenden Schritten: 1. Generate classes Der xjc (xml to java compiler) Befehl generiert aus dem XML Schema die zugehörigen Java Klassen. Der Befehl dazu lautet: xjc.exe -p packageName schema.xsd -d outputDirectory 2. Compile classes Die generierten Klassen müssen kompiliert werden: javac outputDirectory/packageName/*.java 3. Unmarshal Durch den unmarshal Befehl werden die Daten der XML Dokumente gelesen, die entprechenden Java Objekte erzeugt und die gelesenen Daten darin abgespeichert. Das Unmarshalling funktioniert nicht nur für Files sondern auch für DOM Knoten, SAX-Sourcen, String-Buffer, ... Der XML Baum wird dabei gemäss der Definitionen im Schema in eine Java Objekt-Struktur abgefüllt. 4. Validate (optional) Falls erwünscht kann während des unmashallings die eingelesene XML Source gegen das Schema validiert werden. 5. Process content Die Inhalte der Java Struktur können nun beliebig verändert werden. 6. Marshal Die (geänderten) Java Objekte können dann wieder in ein XML Dokument (File, String, DOMKnoten, ...) zurückgeschrieben werden. Während des Schreibens der Daten (marshal) können diese wiederum durch das Schema validiert werden. 55 7.3 Übersetzung verschiedener Basis-Datentypen Die benutzten XML Schema Typen werden in (ziemlich) offensichtlicher Art in Java Typen übersetzt: XML Schema Type Java Data Type xsd:string xsd:integer xsd:int xsd.long xsd:short xsd:decimal xsd:float xsd:double xsd:boolean java.lang.String java.math.BigInteger int long short java.math.BigDecimal float double boolean xsd:byte byte xsd:QName javax.xml.namespace.QName xsd:dateTime javax.xml.datatype.XMLGregorianCalendar xsd:base64Binary byte[] xsd:hexBinary byte[] xsd:unsignedInt long xsd:unsignedShort int xsd:unsignedByte short xsd:time javax.xml.datatype.XMLGregorianCalendar xsd:date javax.xml.datatype.XMLGregorianCalendar xsd:anySimpleType java.lang.String xsd:duration javax.xml.datatype.Duration xsd:NOTATION javax.xml.namespace.QName 56 7.4 Erzeugen der Java Klassen Die Java-Klassen und Member Variablen werden gemäss dem Schema erzeugt: <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://sws.people.org" elementFormDefault="qualified" xmlns="http://sws.people.org"> <xs:element name="people"> <xs:complexType> <xs:sequence> <xs:element name="person" type="personType" maxOccurs="unbounded"/> <xs:element name="address" type="addressType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> Das obige (Teil-) Schema erzeugt eine Klasse People der Form People List<PersonType> person; List<AddresType> address; List<PersonType> getPerson(); List<AddressType> getAddress(); Die folgende Typendefinition <xs:complexType name="personType"> <xs:sequence> <xs:element name="name" type="nameType"/> <xs:element name="born" type="bornType"/> <xs:element name="died" type="diedType"/> . . . </xs:sequence> </xs:complexType> erzeugt eine Java Klasse der Form: PersonType NameType name; BornType born; ... NameType getName(); void setName(NameType name); ... 57 7.5 Einlesen der XML Daten Die vom Framework gelieferte JAXBContext Klasse erstellt einen Zugriff zum JAXB API. Damit wird ein Unmashaller erzeugt. try{ JAXBContext jc = JAXBContext.newInstance(People.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); SchemaFactory schemaFactory = SchemaFactory .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = schemaFactory.newSchema(new File("schema.xsd")); unmarshaller.setSchema(schema); Object obj = unmarshaller.unmarshal( InputStream | File | DocNode | ... ) ... } catch(JAXBException e) { . . . } Für unser People-Beispiel könnte das etwa so aussehen: PeopleType people = (People) unmarshaller.unmarshal( new File("People.xml") ); Durch die erzeugte Java Struktur kann dann mit den beim Binding erzeugten Zugriffsmethoden navigiert werden: for(PersonType b : people.getPerson()) { String firstName = b.getName().getFirstName(); String lastName = b.getName().getLastName(); } 7.6 Schreiben der Daten in ein XML File Zum (formatierten) Schreiben der Daten erzeugen wir mittels des JAXBContext ein Marshaller Objekt. JAXBContext jc = JAXBContext.newInstance(People.class); Marshaller ms = jc.createMarshaller(); ms.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); SchemaFactory sf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = sf.newSchema( new File("Schema.xsd")); marshaller.setSchema(schema); // Schreiben der Daten nach people.xml ms.marshal( people, new FileOutputStream("people.xml")); Dies generiert ein neues File „people.xml“ und scheibt die gesamte (oder Teile der) Java Objekt Struktur in dieses File. Beim Schreiben der XML Daten wird hier die Struktur gegen das Schema validiert. Die Java Objekte können nach Belieben in einen OutputStream, einen Document Node, einen SaxContentHandler, .... geschrieben werden (vgl. JAXB Dokumentation). 58 7.7 Anpassen der generierten Java Klassen und Member Variablen Durch Angabe einer appinfo-Annotation im Schema können die Namen für die generierten Klassen und deren Member-Variablen verändert werden. Die wichtigsten Annotations sind jxb:class und jxb:property. So kann zum Beispiel bestimmt werden, wie die generierte Address-Klasse aussehen soll (vgl. Beispiel auf der Übungsseite) <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://sws.people.org" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.0" elementFormDefault="qualified" xmlns="http://sws.people.org"> <xs:complexType name="addressType"> <xs:annotation> <xs:appinfo> <jxb:class name="Address"/> </xs:appinfo> </xs:annotation> <xs:sequence> <xs:element name="country" type="xs:string"/> <xs:element name="city" type="xs:string"/> </xs:sequence> <xs:attribute name="id" use="required" type="xs:string"> <xs:annotation> <xs:appinfo> <jxb:property name="addressId"/> </xs:appinfo> </xs:annotation> </xs:attribute> </xs:complexType> . . . erzeugt neu eine Klasse Address (statt AddressType) mit einer Member Variablen addressId (statt id). 59 7.8 Externe Binding-Deklaration Statt die gewünschten Java-Typen als Annotations ins Schema File zu schreiben, kann man deren Deklarationen auch in einem separaten File ablegen. Dieses Bindings-File muss dann als Parameter beim Erzeugen der Java Klassen mit angegeben werden (vgl. www.sws.bfh.ch/~amrhein/XML/JAXB/People): xjc -b peopleBindings.xjb -d . -p people people.xsd Ein solches Bindings-File könnte etwa so aussehen: <jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" schemaLocation="People.xsd"> <!-- die hobby-Elemente der Personen sollen in (der „PersonType“ Klasse in) einer Member-Variablen „hobbyList“ abgelegt werden. --> <jxb:bindings node="//xs:complexType[@name='personType']"> <jxb:bindings node=".//xs:element[@name='hobby']"> <jxb:property name="hobbyList"/> </jxb:bindings> </jxb:bindings> <!-- die addressRef Attribute in born sollen in addrRef unbenannt werden --> <jxb:bindings node="//xs:complexType[@name='personType']/xs:sequence/xs:element[@name='born']"> <jxb:bindings node=".//xs:attribute[@name='addressRef']"> <jxb:property name="addrRef"/> </jxb:bindings> </jxb:bindings> <!-- alle firstname elemente sollen nach givenName umbenannt werden --> <jxb:bindings node="//xs:element[@name='firstName']"> <jxb:property name="givenName"/> </jxb:bindings> <!-- die name-Elemente der Personen sollen in einer Klasse <jxb:bindings node="//xs:complexType[@name='nameType']"> <jxb:class name="Name"/> </jxb:bindings> „Name“ abgelegt werden. --> </jxb:bindings> Dies bewirkt, dass die Klasse PersonType neu wie folgt aussieht: PersonType Name name; BornType born; List<String> hobbyList; ... NameType getName(); void setName(NameType name); ... 60 7.9 Erzeugen von Schematas Aus einer Java Klassen-Hierarchie lässt sich umgekehrt mit Hilfe von Annotationen das dazu passende Schema generieren • • • • @XmlType @XmlElement @XmlAttribute ... --> erzeugen eines Schema Types (mit angegebener Reihenfolge) --> erzeugen eines Schema Elements --> erzeugen eines Schema Attributs Der Aufruf zum Erzeugen eines Schemas lautet schemagen.exe package\*.java Ein Beispiel: Die People Klasse import java.util.*; import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(propOrder = { "person", "address" }) @XmlRootElement(name = "people") public class People { @XmlElement(required = true) protected List<Person> person; @XmlElement(required = true) protected List<Address> address; public List<Person> getPerson() { if (person == null) { person = new ArrayList<Person>(); } return this.person; } public List<Address> getAddress() { if (address == null) { address = new ArrayList<Address>(); } return this.address; } } 61 Die Address Klasse import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "addressType", propOrder = { "country", "city" }) public class Address { @XmlElement(required = true) protected String country; @XmlElement(required = true) protected String city; @XmlAttribute(name = "id", required = true) protected String addressId; public String getCountry() { return country; } public void setCountry(String value) { this.country = value; } . . . Das daraus generierte Schema <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema elementFormDefault="qualified" version="1.0" targetNamespace="http://sws.people.org" xmlns:tns="http://sws.people.org" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="people"> <xs:complexType> <xs:sequence> <xs:element name="person" type="personType" maxOccurs="unbounded"/> <xs:element name="address" type="addressType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="addressType"> <xs:sequence> <xs:element name="country" type="xs:string"/> <xs:element name="city" type="xs:string"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:complexType> . . . </xs:schema> 62 7.10 Schreiben von XML Daten ohne ObjectFactory Zum Schreiben von Java Daten in ein XML Dokument braucht es entweder die (beim Binding automatisch generierte) ObjectFactory Klasse oder ein jaxb.index File, in welchem die Namen der annotierten Java Klassen stehen. Das jaxb.index File muss die Klassen Namen (lokale Namen ohne .java oder .class Endung) enthalten und liegt im gleichen Package wie die annotierten Java-Klassen. Mit einem solchen jaxb.index File können wir Java Daten nach XML schreiben, ohne vorher ein Java Binding ausführen zu müssen. Unser Beispiel 63 64 XSLT / XPath / XSL-FO Die Transformations- und Adressierungs-Sprachen für XML http://www.w3.org/TR/xslt20 http://www.w3.org/TR/xpath20 http://www.w3.org/TR/xslfo20 65 Die XSL Sprachen Einleitung, Motivation XSL ist ein vom World Wide Web Consortium empfohlener Standard W3C Recommendation von XSLT2.0 und XPath2.0, sowie Xquery 1.0 vom 23 Januar 2007 XSLT: www.w3.org/TR/xslt20 XPath: www.w3.org/TR/xpath-functions W3C Recommendation des XSL- Formatting Objects Teil vom Dezember 2006 XSL-FO: www.w3.org/TR/xsl11 Vergleich: CSS und XSL Die W3C hat mit dem XSL Standard angefangen, bevor derjenige von CSS (Cascading Stylesheet Language) festgelegt war. Warum gibt es überhaupt zwei Stylesheet Sprachen ? CSS XSL Verwendung mit HTML ja nein Verwendung mit XML ja ja Transformations-Sprache nein ja Syntax CSS XML CSS ist für das Layouting von HTML-Dokumenten geeignet. XSL ist angepasst an die Bedürfnisse von XML. XSL ist viel mächtiger, differenzierter und ausgefeilter als CSS. XSL kann XML- Dokumente beliebig transformieren (also auch in andere Sprachen wie TeX, Java, HTML/CSS, ... umformen) Beide Sprachen können als Stylesheet für XML- Dokumente verwendet werden. CSS und XSL benutzen das gleiche Formatierungs-Modell und beide Sprachen bieten die gleichen Formatierungs-Möglichkeiten. CSS Stylesheet für HTML Files HTML benutzt vordefinierte (und darum allgemein bekannte) Tags: <h1> Titel-Element <p> neuer Abschnitt, ... In CSS wird definiert, wie die verschiedenen HTML Elemente darzustellen sind (Fonts, Farben, ...) h1 { font-family: sans-serif; font-size: 48pt } Die h1 Header sollen mit einer sans-serif Schrift der Grösse 48 ausgegeben werden. p { font-size: 8pt; color: blue } Alle <p> - Elemente sollen in einer 8pt Schrift in blauer Farbe ausgegeben werden. Die Browser kennen diese HTML Tags und verwenden ein CSS (Default-) Stylesheet für deren Darstellung. 66 XSL - Stylesheet für XML XML benutzt keine vordefinierten Tags: <table> kann eine HTML Tabelle oder auch ein Möbelstück sein. Im XML-Dokument steht nicht, wie die Daten dargestellt werden sollen. Es braucht einen Mechanismus, welcher beschreibt, wie die Daten eines XML-Dokumentes dargestellt werden sollen. XSL bietet diesen Mechanismus Das XML Dokument soll nur die reinen Daten enthalten und (im Gegensatz zu HTML) nichts über deren Darstellung (ob als Liste, Tabelle, oder als Fliesstext in Abschnitte unterteilt, ...) aussagen. Mit Hilfe eines XSL Stylesheet wird festgelegt, wie diese Daten dargestellt werden sollen. CSS bietet keine Möglichkeit, Daten zu transformieren oder in Tabellen, Listen, usw. zu verpacken. XSL ist eine Sprache, welche • • • • • XML nach (X)HTML (oder einer beliebigen Sprache) übersetzen kann, XML Daten sortieren oder filtern kann, Spezielle Teile von XML Dokumenten adressieren kann, XML Daten zum Beispiel abhängig vom Wert verschieden formatieren kann (negative Zahlen rot, positive Zahlen schwarz, ...) XML Daten für verschiedene Devices formatieren kann (Bildschirm, Drucker, ...) XSL kann aus den XML Daten nicht nur (neue) XML- Files oder XHTML- Files, sondern beliebige Formate erzeugen. XSL wird oft auch verwendet, um die Daten des XML- Dokuments anders zu strukturieren (zum Beispiel passend zu einem anderen Schema oder DTD). Obwohl XSL eigentlich aus drei Teilen mit drei verschiedenen Namen zusammengesetzt ist, wird oft einfach für alles der Name XSL benutzt. Eine Transformationssprache --> XSLT (XSL Transformation) Eine Adressierungssprache --> XPath (Adressieren und Selektieren) Eine Formatierungssprache --> XSL-FO (XSL Formatting Objects) 67 8 Die Transformations-Sprache XSLT • XSLT ist der wichtigste Teil des XSL Standards. XSLT wird benutzt, um XML Dokumente in andere XML-, HTML-, ... Dokumente umzuformen. • XSLT kann auch neue, nicht im XML-Dokument enthaltene Daten-Elemente in das AusgabeDokument einfügen (zum Beispiel Formatier-Befehle), Elemente umsortieren oder weglassen • XSLT kann die XML-Knoten (zum Beispiel auch abhängig com Wert des Elementes) unterschiedlich behandeln. 8.1 Wie funktioniert XSLT? • Ein XSL File besteht aus Regeln (Templates), welche bestimmen, wie die einzelnen Knoten eines XML- Dokuments umgeformt werden sollen. • XSLT benutzt im Transformations-Prozess die Sprache XPath, um die zu einer Regel (Template) passenden Knoten des XML Dokuments heraus zu filtern. • Wenn im XML- Dokument ein passender Knoten (match) gefunden wird, formt XSLT diesen Knoten mit Hilfe der angegebenen Regel um, und schreibt das Resultat ins Ausgabe-Dokument. • Alle Text-Knoten des XML- Dokumentes, welche nicht zu einer Regel passen, werden unmodifiziert in das Ausgabe-Dokument kopiert (vgl. Default Template Rules). 68 Das Beispiel Dokument: People.xml <?xml version="1.0"?> <people> <person> <name> <firstName>Alan</firstName> <lastName>Turing</lastName> </name> <born date="1912" addressRef="gbl"/> <died date="1954" addressRef="gbc"/> <profession>computer scientist</profession> <profession>mathematician</profession> <profession>cryptographer</profession> <hobby>philosophy</hobby> <hobby>biology</hobby> </person> ... Das vollständige XML- Dokument finden Sie auf dem Zusatzblatt. Verbinden mit einem Stylesheet Im XML Dokument kann mit Hilfe der Prozess-Instruktion <?xml-stylesheet ... ?> das zu verwendende XSL-Stylesheet angegeben werden. <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="http://www.abc.ch/mystyle.xsl"?> <people> <person> <name> <firstName >Alan</firstName> <lastName>Turing</lastName> </name> ... Falls das XML-Dokument nicht statisch zu einem Stylesheet verbunden werden soll, kann zum Beispiel auch ein kleines Javaskript geschrieben werden, welche die beiden Teile verbindet. Die URL des Stylesheets kann relativ sein, also z.Bsp. href="mystyle.xsl" (wie im obigen Beispiel) als absolute Adresse. 69 8.2 Der Aufbau eines Stylesheets: Templates XSL benutzt Templates (Regeln), um zu bestimmen, wie die XML-Knoten umgeformt werden sollen. Ein "match"-Attribut wird benutzt, um ein Knoten einem Template zuzuordnen. <xsl:template match= Knoten > Action </xsl:template> Der Befehl xsl:template legt zuerst die Liste (sequence) aller Knoten des XML-Dokuments an, welche durch match adressiert werden. Die Elemente der Liste werden dann der Reihe nach gemäss den Regeln in Action transformiert. Die Sprache XPath dient hier zur exakten Adressierung der Knoten (welche Knoten sollen umgeformt werden). Das Resultat von Action (der Transformation) wird in das Ausgabe-Dokument geschrieben. Ein erstes XSL Beispiel: hello world Da das Stylesheet selber auch ein XML Dokument ist, fängt es mit einer xml-Deklaration an: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="people"> hello world </xsl:template> </xsl:stylesheet> • Der xsl:stylesheet Befehl in der zweiten Zeile ist der eigentliche Start des Stylesheets. • Der Namespace URI für xsl muss genau so angegeben werden. • Das <xsl:template> Element definiert den Start eines Templates. • Das Template Attribute match="people" adressiert das (alle) people-Element(e) des XMLDokumentes. • Zwischen dem Start- und dem End-Tag von xsl:template stehen die Anweisungen, welche für das adressierte Element auszuführen sind. • Das Tag <xsl:output> definiert, wie das Output File aussehen soll. Hier ist die Ausgabe ein UTF-8 codiertes Text- File. • Die Ausgabe des Stylesheets ist „hello world“. 70 Fortsetzung: hello world Wir ergänzen das obige Beispiel um eine weitere Regel: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="people"> hello world </xsl:template> <xsl:template match="person"> I am here </xsl:template> </xsl:stylesheet> Trotz der zusätzlichen Regel für person lautet die Ausgabe wiederum bloss hello world. Damit die zweite Regel ausgeführt wird, müsste sie im people-Template explizit aufgerufen werden. Fortsetzung: hello world Wir löschen die people Regel aus dem obigen Beispiel: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="person"> I am here </xsl:template> </xsl:stylesheet> In diesem Stylesheet fehlt die people-Regel. Das bedeutet, dass die Default Regel zum Einsatz kommt, welche alle (direkten) Nachfolger des Root-Knotens (people) sucht, im Stylesheet nach Regeln dafür sucht, und diese ausführt. Da das people Element mehrere person und address Kinder hat, werden person und address Regeln gesucht und ausgeführt. Die "person"- Regel wird für jedes gefundene <person>- Element ausgeführt. Es gibt im Stylesheet keine Regel für address-Elemente, also wird die Default Regel ausgeführt. Diese kopiert alle Text-Kinder von <address> (vgl. Kapitel: Default Template Regeln) Die Ausgabe ist darum: I am here I am here I am here I am here Great Britain London Germany Ulm USA Queens, NY Switzerland Basel Great Britain Cambridge USA Princeton 71 8.3 Die wichtigsten XSLT Befehle Es kann hier nur eine Auswahl von XSL Befehlen und Konstrukten behandelt werden. Die ganze Liste der Befehle mit Beispielen finden Sie zum Beispiel auch unter http://www.saxonica.com/documentation xsl:apply-templates, xsl:call-template, xsl:for-each Welches Kind als nächstes behandelt werden soll, kann explizit durch ein select-Attribut ausgewählt werden <xsl:apply-templates select="person"/> Der Befehl xsl:apply-templates • sucht im XML den angegebenen Pfad, • eine Regel dazu und • führt diese aus. Der Befehl <xsl:apply-templates/> sucht alle Kind (-Elemente) des aktuellen Knotens und führt deren Templates aus. (Mit "Kind" sind jeweils nur die direkten Nachfolger gemeint). Beispiele: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="people"> <xsl:apply-templates select="person"/> </xsl:template> <xsl:template match="person"> I am here </xsl:template> </xsl:stylesheet> Die gleiche Ausgabe erhalten wir durch Aufruf der folgenden Templates: <xsl:template match="people"> <xsl:call-template name="templ1"/> </xsl:template> <xsl:template name=" templ1"> <xsl:apply-templates select="person"/> </xsl:template> <xsl:template match="person"> I am here </xsl:template> Oder als dritte Möglichkeit: <xsl:template match="people"> <xsl:for-each select="person"> I am here </xsl:for-each> </xsl:template> 72 xsl:value-of Das XSL <xsl:value-of> Element kann benutzt werden, um den Wert bestimmter Knoten auszugeben. Beispiele: <xsl:value-of select="lastName"/> <xsl:value-of select="firstName"/> <xsl:value-of select="hobby" separator=", " /> value-of berechnet den Wert eines Ausdrucks oder XML Knotens nach den folgenden Regeln: • • • • Element: der Text-Inhalt eines Elementes, nachdem alle Referenzen aufgelöst sind. Text: der Text selber Attribut: der Wert des Attributes <xsl:value-of select=" . "/> gibt den Wert des aktuellen Knotens zurück. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xhtml" encoding="UTF-8"/> <xsl:template match="people" > <html> <head> <title>Famous Scientists</title> </head> <body> <h4> Famous Scientists</h4> <xsl:apply-templates select="person"/> </body> </html> </xsl:template> Die Ausgabe der Transformation: <html> <head><title> Famous Scientists</title></head> <body> <h4> Famous Scientists</h4> <p>Turing Alan</p> <p>Feynman Richard</p> <p>Einstein Albert</p> <p>Euler Leonhard</p> </body> </html> <xsl:template match="person"> <p><xsl:apply-templates select="name"/></p> </xsl:template> <xsl:template match="name"> <xsl:value-of select="lastName"/> <xsl:text> </xsl:text> <xsl:value-of select="firstName"/> </xsl:template> </xsl:stylesheet> • Das obige XSL-Stylesheet erzeugt ein HTML-Dokument. Die HTML-Tags gehören nicht zu XSL. Sie werden darum einfach in die Ausgabe kopiert (literal result element). • Das people Template verpackt den Output in ein HTML-Dokument, mit head, title und body. Danach wird das person Template aufgerufen. • Das person- Template fängt einen neuen Abschnitt an (<p>) und ruft das name- Template auf. • Im name Template wird durch das <xsl:text> Element sichergestellt, dass zwischen dem Namen und dem Vornamen ein Abstand (Space) eingefügt wird. <xsl:text>mytext</xsl:text> kopiert den String mytext (unverändert) in das Ausgabe-Dokument (inkl. Whitespace). 73 Adressieren von Attributen Für das Adressieren von Attributen wird das Zeichen @ verwendet. <xsl:value-of select=“born/@date“/> liefert den Wert des date Attributes im born Element. <xsl:template match="person"> <b><xsl:apply-templates select="name"/></b> <xsl:text> </xsl:text> <small> ( <xsl:value-of select="born/@date"/> <xsl: value-of select="died/@date"/>) </small> <p/> </xsl:template> Template mit Modes Es ist möglich, für denselben Knoten mehrere verschiedene Templates zu definieren, die dann kontextabhängig angewendet werden. Der Aufruf des Templates sowie das Template selber erhalten ein mode Attribute. Damit können zum Beispiel Inhaltsverzeichnisse, Querverweise oder verschiedene Darstellungen der gleichen Datenelemente erzeugt werden. <xsl:stylesheet version="1.0 " xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="people"> <html> <head> <title> Famous Scientists</title> </head> <body> <h3> Famous Scientists, Mode</h3> <h4> Table of Contents </h4> <ul> <xsl:apply-templates select="person" mode ="toc"/> </ul> <xsl:apply-templates select="person"/> </body> </html> </xsl:template> Der erste Durchgang wird durch <xsl:template match="person" mode="toc"> gebildet, der zweite durch <xsl:template match="person"> <xsl:template match="person" mode ="toc"> <xsl:apply-templates select="name" mode="toc"/> </xsl:template> <xsl:template match="person"> <p> <xsl:apply-templates select="name"/> <small> (<xsl:apply-templates select="born/@date"/> <xsl:apply-templates select="died/@date"/> ) </small> </p> </xsl:template> <xsl:template match="name" mode ="toc"> <li> <xsl:value-of select="lastName"/> </li> </xsl:template> <xsl:template match="name"> ... --> vgl. Beispiel vorher </xsl:template> </xsl:stylesheet> 74 xsl:if <xsl:if test="lastName = 'Euler'"> ... </xsl:if> filtert nur die Elemente mit lastName gleich Euler. <xsl:if test="born/@date &gt; 1900" > ... </xsl:if> behandelt nur die Personen, welche nach 1900 geboren sind. Im Template: <xsl:template match="person"> <xsl:if test="born/@date > 1900 "> <xsl:apply-templates select="name"/> </xsl:if> </xsl:template> Die Bedingungen können mit Hilfe von and oder or verbunden werden. <xsl:template match="person"> <xsl:if test="born/@date > 1850 and name/firstName != 'Alan' "> <xsl:apply-templates select="name"/> </xsl:if> </xsl:template> Das Template gibt nur die Namen der Personen aus, welche später als 1900 geboren sind. Komplizierte Bedingungen können mit runden Klammern gruppiert werden: ( ... and ... ) or ( ... or ... <xsl:template match="person"> <xsl:if test="born/@date > 1900 or (name/firstName = 'Leonhard' and name/lastName='Euler')" > <xsl:apply-templates select="name"/> </xsl:if > </xsl:template> 75 xsl:choose Es gibt in XSL kein if-else Konstrukt. Anstatt eines if-else gibt es ein xsl:choose. Verzweigungen werden aus xsl:when und xsl:otherwise zusammengesetzt. <xsl:choose> <xsl:when test="..."> ... </xsl:when> <xsl:otherwise> ... </xsl:otherwise> </xsl:choose> xsl:choose ist vergleichbar mit einem Switch-Case-Statement in Java oder C++. Nur der erste when-Ast, welcher zu true evaluiert, wird ausgeführt. Falls keiner der when-Tests zu true evaluiert, wird otherwise ausgeführt. Ein Beispiel <xsl:template match="people"> . . . vgl. Beispiel vorher <xsl:apply-templates select="person"/> . . . </xsl:template> <xsl:template match="person"> <xsl: choose > <xsl: when test="profession='mathematician' "> <font color="blue"> <xsl:apply-templates select="hobby"/> </font> </xsl: when > <xsl: when test="profession='cryptographer' "> <font color="red"> <xsl:apply-templates select="hobby"/> </font> </xsl: when > <xsl: otherwise > <xsl:apply-templates select="hobby"/> </xsl: otherwise > </xsl: choose > </xsl:template> blau für Mathematiker rot für Kryptologen sonst schwarz <xsl:template match="hobby"> <li><xsl:value-of select="."/></li> </xsl:template> 76 xsl:sort Um eine Sequenz von Knoten während der Transformation (nach einem gewählten Schlüssel) zu sortieren, gibt es das sort Element: <xsl:sort select="name/lastName"/> xsl:sort kann entweder innerhalb eines <xsl:apply-templates> oder eines <xsl:for-each> Elements vorkommen. Das select-Attribut gibt an, nach welchem Schlüssel sortiert werden soll. Falls numerisch sortiert werden soll (der Sortier-Schlüssel eine Zahl ist) muss dies mit dem data-type Attribut angegeben werden: <xsl:sort select = "..." data-type="number" /> Beispiel: Sortieren mit xsl:apply-templates oder mit xsl:for-each <xsl:template match="people"> <html> <head> <title> Famous Scientists, xsl:sort</title> </head> <body> <h3>Famous Scientists</h3> <xsl: apply-templates select="person"> Alle Personen sortiert aufzählen <xsl:sort select="name/lastName" order="ascending"/> </xsl:apply-templates> <br/> <b>Hobbies of the Famous</b> <ul> <xsl: for-each select="person/hobby"> Alle Hobbies sortiert aufzählen <xsl:sort select="." order="descending"/> <li><xsl:value-of select="."/></li> </xsl:for-each> </ul> </body> </html> </xsl:template> Der apply-templates Befehl sammelt alle Knoten unter dem XPath (hier person). Der Sort-Befehl bewirkt, dass alle person-Elemente vor deren Verarbeitung nach ihrem Namen sortiert werden. Der for-each Befehl sammelt hier alle Hobby Elemente aller Personen und sortiert sie vor deren Ausgabe. 77 xsl:variable In XSL können auch Konstanten definiert werden: <xsl:variable name="ntext"> value_of_variable </xsl:variable> Der Wert der Konstanten ntext kann mit $ntext gelesen werden: <xsl:value-of select="$ntext"/> Der Wert von ntext kann dann nicht mehr verändert werden! Dies sind keine Variablen, wie man sie in C++ oder Java kennt, sondern eher von der Art der Variablen wie man sie im funktionalen Programmieren kennt (benannte Konstanten). Trotzdem können diese Variablen sehr nützlich sein. Beispiel: Berechnen einer gewichteten Summe Wir nehmen das Beispiel einer Bestellung. <?xml version="1.0"?> <order> <book> <title>XML</title> <price>24.20</price> <quantity>2</quantity> </book> <book> <title>UML</title> <price>43.20</price> <quantity>1</quantity> </book> <book> . . . </book> </order> Von dieser Bestellung wollen wir den Gesamt-Preis berechnen. <xsl:template match="totalBetrag"> <xsl:variable name="tmp"> <tmpPrice> <xsl:for-each select="book"> <item> <xsl:value-of select="price * quantity"/> </item> </xsl:for-each> </tmpPrice> </xsl:variable> <xsl:value-of select="sum($tmp/tmpPrices/item)"/> </xsl:template> Der Inhalt der Variablen tmp ist dann: Der sum Befehl zählt diese Werte zusammen. 78 Auflösen von key / keyRef Paaren mit Hilfe von Variablen <xsl:template match="born"> <xsl:value-of select="../name/lastName"/>, <xsl:value-of select="name()"/> <xsl:text> </xsl:text> <xsl:value-of select="@date"/> <xsl:text> in </xsl:text> <xsl:variable name="myref" select="@addressRef"/> <xsl:value-of select="//address[@id=$myref]/city"/> </xsl:template> Address-ID in der Variable abspeichern die gefundene Adresse ausgeben Dieses Vorgehen ist allerdings nicht effizient. Wir werden später ein besseres Verfahren kennen lernen. xsl:param Beim Aufruf von Templates können Parameter übergeben werden: <xsl:apply-templates select="title"> <xsl:with-param name="p1"> <xsl:value-of select="irgendein Wert"/> </xsl:with-param> </xsl:apply-templates> Verwendung im Template: <xsl:template match="title"> <xsl:param name="p1"/> ... verwenden von $p1 ... </xsl:template> Es können auch mehrere Parameter übergeben werden: <xsl:template match="people"> <xsl:apply-templates select="person"> <xsl: with-param name="a1"> <xsl:value-of select="address[1]/city"/> </xsl:with-param> <xsl: with-param name="a2"> <xsl:value-of select="address[2]/country"/> </xsl:with-param> </xsl:apply-templates> </xsl:template> Der Wert der Parameter kann im Template (analog zu den Variablen) mit $a1, bzw. $a2 gelesen werden. Ausserdem kann ein Default-Wert angegeben werden, falls im Aufruf ein Parameter fehlt. <xsl:template match="person"> <xsl:param name="a1"/> beide Parameter einlesen <xsl:param name="a2"> Germany Default Wert für a2 </xsl:param> <xsl:value-of select="name/lastName"/> <xsl:value-of select="$a1"/> <xsl:value-of select="$a2"/> </xsl:template> 79 xsl:for-each-group Das folgende Template gibt alle Adressen gruppiert nach Ländern aus. Adressen aus dem selben Land werden durch ein Komma separiert. <xsl:template match="people"> <xsl:for-each-group select="address" group-by="country"> <xsl:value-of select="country"/>: <xsl:value-of select="current-group()/city" separator=", "/> <br/> </xsl:for-each-group> </xsl:template> Die Ausgabe ist dann: Great Britain: London, Cambridge Germany: Ulm USA: Queens, NY, Princeton Switzerland: Basel Importieren von Stylesheets Die Befehle <xsl:import href="URI"> und <xsl:include href="URI"> fügen das unter der angegebenen Adresse gefundene Stylesheet ein. Der Pfad kann dabei relativ <xsl:include href="ss.xsl"/> oder absolut sein: <xsl:include href="http://www.sowieso.ch/ss.xsl"/> Eingefügte Stylesheets können selber ebenfalls wieder mit xsl:include oder xsl:import andere Stylesheets einlesen (Vorsicht: Zyklen!). Beim Transformieren werden sowohl die Regeln des importierten als auch des importierenden Stylesheets angewandt. Durch <xsl:import> werden die Regeln beider Stylesheets benutzt. Falls sich Regeln widersprechen wird die Regel des lokalen Stylesheets benutzt. <xsl:include> kopiert den Inhalt des angegebenen Stylesheets in das einlesende Stylesheet. Falls sich Regeln widersprechen (durch Templates mit gleichen Namen oder gleichen Pattern), tritt normalerweise ein Fehler auf. 80 9 Grundlegendes zu XSLT 9.1 Default Template Regeln Was geschieht mit den Knoten, wenn es im Stylesheet dafür kein Template gibt? Auch wenn im Stylesheet keine explizite Regel für die Behandlung eines Knotens angegeben ist, gibt es (abhängig vom Typ des Knotens) eine Regel für diesen Knoten. Es gibt sieben Typen von Knoten in XML-Dokumenten • Root-Knoten • Element-Knoten • Attribut-Knoten • Text-Knoten • Kommentar-Knoten • Verarbeitungs-Anweisungen (Processing-Instructions) • Namespace-Knoten Für jeden Knoten-Typ gibt es eine Default Template Regel, welche angibt, was mit einem solchen Knoten passieren soll, falls im Stylesheet keine Regel dafür vorhanden ist. Text- und Attribut-Knoten Von Text- und Attribut-Knoten wird der Wert des Textes (des Attributes) in das Ausgabe-Dokument kopiert. Als XSL-Template sieht dies wie folgt aus: <xsl:template match= " text() | @* "> <xsl:value-of select="."/> </xsl:template> Wir können uns vorstellen, dass in jedem Stylesheet diese Regel mit drin steht (auch wenn sie nicht explizit vorhanden ist). Die folgende Regel: <xsl:template match="name"> <xsl:apply-templates select="firstName"/> <xsl:text> </xsl:text> <xsl:apply-templates select="lastName"/> </xsl:template> sagt zum Beispiel nichts darüber aus, was mit dem (Text-) Inhalt des firstName-, bzw. lastName-Knotens (Alan, Euler, Turing, ...) geschehen soll (falls es kein entsprechendes Template gibt). Falls es für firstName oder lastName keine Regel im Stylesheet gibt, wird automatisch die Default-Regel angewandt, welche den Text (Alan, Euler, Turing, ...) unverändert in die Ausgabe kopiert. 81 Root- und Element-Knoten Vom Root- und von allen Element-Knoten werden per Default die Template-Regeln aller Kindknoten aufgerufen. Als XSL-Template sieht dies wie folgt aus: <xsl:template match= " / | * "> <xsl:apply-templates/> </xsl:template> Falls es also im Stylesheet keine Regel für das Root-Element oder für "/" gibt, wird die Regel für dessen Kind-Knoten gesucht und ausgeführt. Falls es im Stylesheet für einen beliebiges Element keine Regel gibt, werden ebenfalls dessen Kind-Knoten gesucht und deren Regeln ausgeführt. Kommentare und Verarbeitungsanweisungen Falls es keine explizite Regel dafür gibt, werden Kommentare und Processing Instructions nicht ausgegeben. Als XSL-Template sieht dies wie folgt aus: <xsl:template match= "processing-instructions() | comment()" /> Es gibt keine Adressierung (keinen XPath Ausdruck) für Namespace-Knoten. Kommentare, Namespaces und Verarbeitungsanweisungen machen im Ziel Dokument normalerweise keinen Sinn mehr. 82 9.2 Template Driven vs. Data Driven Stylesheets In allen Beispielen, welche wir bisher betrachtet haben, bestimmt das Template im Stylesheet, welche Regel als nächstes benutzt werden soll. Diesen Programmierstil bezeichnet man als Template-Driven. Dies ist immer dann sinnvoll, wenn die Source Dokumente stark struktuiert sind. Daten-bezogene XML- Dokumente sind üblicherweise stark strukturiert. Anders sieht es aus, wenn die XML Dokumente keinen strikten Aufbau haben. Dies findet man zum Beispiel in Dokumenten, welche Texte, Briefe oder Webseiten Inhalte beschreiben. Ein Beispiel XML Dokument, ohne strikten Aufbau <?xml version='1.0'?> <book-review> <title>This Book</title> by <author>This Author</author>, published by <publisher>The Publisher</publisher> on <date>date</date>, is indeed a good book. However, the book titled <title> Bad Book</title> by the same publisher is very bad. The reviewer is left to wonder whether this is because <title>Bad Book</title> was written by <author>Bad Author</author>, or because it was published on <date>date</date>. </book-review> Für solche Dokumente ist es sehr schwierig, im Stylesheet die Reihenfolge der zu benutzenden Templates festzulegen. Viel einfacher ist es, das Stylesheet Data-Driven zu implementieren: d.h. xsl:apply-templates ohne select-Attribut. Im folgenden Stylesheet bestimmt allein das XML Dokument, in welcher Reihenfolge die Regeln aufgerufen werden. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html><body> <xsl:apply-templates/> </body></html> </xsl:template> <xsl:template match="book-review"> <p> <xsl:apply-templates/> </p> </xsl:template> <xsl:template match="title"> <span style="font-weight:bold"> <xsl:value-of select="."/> </span> </xsl:template> Die Ausgabe <xsl:template match="author"> <span style="font-style:italic"> <xsl:apply-templates/> </span> </xsl:template> <xsl:template match="publisher"> <span style="color:blue"> <xsl:apply-templates/> </span> </xsl:template> ... </xsl:stylesheet> Die beiden Modelle (Template-Driven und Data-Driven) lassen sich in einem Stylesheet nach Bedarf mischen. 83 10 XPath: Die XSL Adressierungssprache Die Templates im Stylesheet können nur dann richtig (an der richtigen Stelle) angewandt werden, wenn die Adressierung (match) korrekt ist. XSL muss beliebige Knoten des XML-Dokumentes adressieren (identifizieren) können. Dafür benutzt XSL die Sprache XPath. Die W3C Spezifikation für XPath finden Sie unter www.w3.org/TR/xpath20/ Einige Beispiele name alle <name> Kinder des aktuellen Knotens name/firstName alle <firstName> Kinder aller / der Root Knoten des Dokumentes . der aktuelle Knoten .. der Vorgänger (Parent) des aktuellen Knotens //name alle <name> Elemente des Dokumentes .//name alle <name> Nachfolger (direkte und indirekte) des aktuellen Knotens <name> Kinder des aktuellen Knotens //name/firstName alle <firstName> Elemente, welche Nachfolger von <name> sind. // ist die Abkürzung für „descendant-or-self“, d.h. für alle direkten und indirekten Nachfolger, sowie den Knoten selber. 10.1 Die XPath Syntax • Die XPath-Syntax sieht ähnlich aus wie die übliche Filesystem-Adressierung. • Das Pattern matching ist abhängig vom Kontext: Ein XPath Ausdruck kann verschiedene Knoten bezeichnen, abhängig von der Stelle im Dokument, in welchem er steht. • XPath findet alle passenden Knoten. Sollen nur gewisse Knoten aus dem Pfad selektiert werden, kann der Pfad (durch Prädikate) eingeschränkt werden. • Ein XPath ist absolut, falls er mit einem Slash (" / ") anfängt, z.B. /person • Ein Pfad ist zusammengesetzt auf einer Reihe von Schritten, welche durch "/" getrennt sind: people/person/born • Attribute werden durch "@" markiert. born/@addressRef • Pfade können kombiniert werden durch "|" (Auswahl) name/firstName | profession | hobby 84 Wildcards Pfade können die Wildcards *, @* und node() enthalten. * @* node() • • • selektiert alle Elemente selektiert alle Attribute selektiert alle Knoten Attribute, Text-Knoten, Kommentare oder Verarbeitungsanweisungen (Processing-Instructions) werden durch den Stern (*) nicht selektiert. @* selektiert nur Attribute node() selektiert alle Knoten: Elemente, Attribute, Text-Knoten, Kommentare, Verarbeitungsanweisungen, … Beispiele //* /* str/*/title //*/@id //*/@* findet alle Elemente im Dokument alle Kinder von Root (also das Root-Element) alle <title> Enkel von str alle Attribute mit Namen id alle Attribut Knoten im Dokument. XPath Prädikate XPath kann auch filtern. Dazu wird im select-Attribut zum Pfad ein Prädikat mit angegeben <xsl:apply-templates select="person[name/firstName != 'Alan']"/> Mögliche Filter-Operatoren sind: = gleich != ungleich &lt; kleiner als > grösser als XPath Filter können xsl:if Statements ersetzen und sind oft eleganter und effizienter. Prädikate können in select Attributen, aber auch in match Attributen verwendet werden: Die Regel <xsl:template match=“person[@id]“> gilt nur für person-Elemente, welche ein id-Attribut haben. (Vorsicht: für die anderen person-Elemente benötigen Sie also eine separate Regel!) XPath Prädikat Beispiele: born[ @addressRef = 'gbc' ] alle <born> Kinder, welche ein addressRef Attribut mit Wert "gbc" haben died[ @addressRef ] alle <died> Kinder, welche ein addressRef Attribut haben person [born/@date &gt; 1900 ]/name alle Namen von Personen, welche später als 1900 geboren sind name[last()] das letzte <name> Kind des aktuellen Knotens 85 Zu beachten ist, dass die Adressierungen vom Kontext abhängig sind: died[@addressRef] gibt nur dann ein Element zurück, wenn der aktuelle Knoten (Kontext) ein oder mehrere died Kinder hat, welche ein addressRef Attribut haben. Doppelte Einschränkungen (nacheinander ausgeführt) name[last()][@addressRef] gibt das letzte name-Element zurück, falls dieses ein addressRefAttribut hat. name[@addressRef][last()] gibt das letzte name-Element zurück, welches ein addressRef-Attribut hat. XPath Schritte Jeder Schritt im Pfad ist ein Bezeichner, ein Wildcard oder ein Prädikat. <xsl:template match="person"> <xsl:apply-templates select="*[@date]"/> </xsl:template> <xsl:template match="born | died"> <xsl:value-of select="../*/lastName"/>, <xsl:value-of select="name(.)"/>: <xsl:value-of select="@date"/> <xsl:variable name="var" select="@addressRef"/> <xsl:apply-templates select="//*[@id=$var]/city"/> <br/> </xsl:template> <xsl:template match="city"> in <xsl:value-of select="."/> </xsl:template> Die verwendeten Bezeichner --> people, name, @date, ... Wildcards --> *, @*, node() Prädikate • Der Pfad *[@date] wählt alle Kind-Knoten von person aus, welche ein date Attribut haben. • Der Pfad // * [@id = $var] wählt alle Elemente Dokumentes aus, welche ein id-Attribut haben, welches den gleichen Wert hat wie var (bzw. den gleichen Wert wie @addressRef). Gefunden werden die address Elemente und es wird der Inhalt des <city>-Elements ausgegeben. Weitere Beispiele von Pfaden mit Prädikaten: //name/* alle Kinder von name //name/*[@*] alle Kinder von name, die ein Attribut haben //name/*[*] alle Kinder von name, welche keine Blätter sind //*[@*] alle Elemente, die ein Attribut haben //person[*/@id]/name alle name Kinder von person Elementen, die ein Kind mit einem id Attribut haben //*[@*]/* alle Kinder von Elementen, die ein Attribut haben 86 87 10.2 Adressierungs-Achsen preceding-sibling sind alle früheren, following-sibling alle im Dokument nachfolgenden Geschwister-Knoten von self. Die Begriffe preceding und following beziehen sich auf die Reihenfolge im XML-Dokument. following sind alle Knoten, welche im XML-Dokument hinter dem End-Tag des self-Knoten stehen. preceding sind alle Knoten, die vor dem Start-Tag des self-Knoten stehen und keine ancestors sind. Beispiele: child::people alle <people>-Kinder des aktuellen Knotens following-sibling::profession alle <profession>- Geschwister des aktuellen Knotens, welche im XMLFile später auftreten. descendent::* alle (direkten und indirekten) Nachfolger des aktuellen Knotens ancestor::*/child::person alle <person> Kinder aller Vorgänger Knoten des aktuellen Knotens Für die wichtigsten Adressierungs-Achsen existieren (die bereits bekannten) Abkürzungen: child (leer) parent .. self . descendant-or-self // Die Bezeichnung "child::" kann immer weggelassen werden. /child::people/child::person/child::profession = /people/child::person/profession = /people/person/child::profession self wird vor allem im Zusammenhang mit value-of benutzt: <xsl:value-of select=" self::* "/> ist gleichbedeutend mit <xsl:value-of select=" . "/> descendant-or-self kann absolut oder relativ verwendet werden: <xsl:apply-templates select= "//lastName”/>, d.h. alle lastName Elemente des Dokuments <xsl:apply-templates select= "person//lastName”/>, d.h. nur die lastName Nachfolger von person. Das Template <xsl:apply-templates select= "//lastName[string-length(text()) > 6]/.. "/> findet zum Beispiel alle parent Knoten von allen lastName Elementen, deren Text-Inhalt mehr als 6 Buchstaben lang ist. 88 10.3 Die wichtigsten XPath Funktionen Die W3C Recommendation zu XQuery 1.0 and XPath 2.0 Functions and Operators finden Sie unter http://www.w3.org/TR/xquery-operators Ausserdem finden Sie alle von saxon8 unterstützten XPath 2.0 Funktionen und Operatoren unter http://www.saxonica.com/documentation Einige Beispiele boolean( obj ), number( obj ), string( obj ) konvertiert das object obj falls möglich zu einem boolean/number/string ... boolean(0) ist falsch, boolean(5) ist wahr boolean(NaN) ist falsch, also auch boolean(number('a')). (NaN steht für „not a Number“.) boolean not (value) gibt true zurück, falls value zu false evaluiert (und umgekehrt). Funktionen auf Strings String concat( String s1, String s2, String s3, ... ) setzt die Strings s1, s2, s3, ... zusammen String substring(string s1, double start, double length) gibt den Teil-String von s1 ab Position start mit Länge length zurück. boolean contains( string s1, string s2 ) gibt true zurück, falls s2 ein Substring von s1 ist number string-length( string ) gibt die Länge des Strings zurück string translate( string s, string s1, string s2 ) ersetzt alle Vorkommnisse von s1 in s durch s2. boolean matches(string, pattern) gibt true zurück, falls der gegebene String das Pattern erfüllt. String replace(string1, pattern, string2) ersetzt in string1 alle Teile, welche das pattern erfüllen durch string2. Numerische Funktionen number abs(number), number avg(number), number max(items), number min(items) Absolutwert, Durchschnitt, Maximum, Minimum number round(number), number ceiling(number), number floor(number) Runden, Aufrunden, Abrunden atomicType sum(atomicType) summiert die Werte aller Werte auf. atomicType ist muss hier eine Zahl oder eine Zeitdauer (duration) sein. Falls ein Argument-Knoten von sum keine Zahl oder Zeitdauer ist, ist der Rückgabe-Wert "NaN" (Not a Number). sum(//number) summiert alle Werte unter //number, sum(//book/quantity) berechnet also zum Beispiel die Anzahl vorhandener Bücher. String format-number(value, pictureString) formatiert den value gemäss dem pictureString. format-number(500100, ' #.0 ') Ausserdem existieren die normalen Grundoperationen +, -, *, mod und div. 89 Funktionen auf Sequenzen (Listen von Knoten) number last() berechnet die Anzahl Knoten im Kontext //person[1] gibt die erste, //person[last()] gibt die letzte Person zurück. number position() gibt die Position des Kontext Knotens in der aktuell verarbeiteten Sequenz zurück. Während dem Abarbeiten der Sequenzen wir die Position laufend aktualisiert. Falls die Personen gefiltert oder sortiert werden, liefert position dennoch eine normale aufsteigende Nummerierung. number count( items) zählt die Knoten in der Sequenz items. z.B. count(//person) gibt 4, count(//address) gibt 6 zurück. remove(...), insert-before(...), reverse(...), subsequence(...) sind Funktionen auf Sequenzen zum Löschen, Einfügen, Umdrehen und Zerteilen der Sequenzen. boolean empty(items) true, falls items eine leere Sequenz ist. Datum und Zeit date current-date(), dateTime currentTime() gibt das aktuelle Datum, bzw. die aktuelle Zeit zurück. Dieses kann nachher durch format-date(), bzw. format-dateTime() formatiert werden. String format-date(date, pictureString) formatiert das Datum date gemäss pictureString. format-date(current-date(), ' [D]-[M]-[Y] ') for Ausdrücke Mit Hilfe von „for“ Ausdrücken ist es möglich, in einem XPath Aufruf über alle Elemente der ganzen Sequenz zu iterieren. Die Syntax benutzt eine Iterator-Variable (hier z.B. x). Aus einem for $x in E return result evaluiert result für jedes Element der Sequenz E <xsl:value-of select="sum(for $x in book return $x/price * $x/quantity)"/> summiert die price * quantity Werte aller book Elemente auf. 90 Einlesen von XML Dokumenten Die Funktion document($srcval ) lädt das an der Adresse (URI) srcval gefundene Dokument. Durch Definition einer (globalen) Variable "input" kann folgendermassen auf die Elemente des Files "Daten.xml" zugegriffen werden. <xsl:variable name="input" select="document('Daten.xml')/person"/> Diese Variable kann dann benutzt werden um die Namen der Personen im Daten File zu lesen: <xsl:value-of select="$input/name"/> xsl:key und key()-Funktion Durch xsl:key kann eine Indexierung (Schlüssel) von Elementen angelegt werden. Dies erzeugt eine Hash-Tabelle, über deren Schlüssel auf die Werte zugegriffen werden kann. Die key-Funktion hat zwei Auswirkungen: Sie kann den xsl-Code vereinfachen und sie kann die Performance positiv beeinflussen, indem der Parser eine Indexierung für alle key-Werte anlegt. Alle ../born/@date-Attribute werden als Schlüssel für die name-Elemente abgelegt: <xsl:key name="myKey1" match="name" use="../born/@date"/> Alle hobby-Elemente werden unter ihrem Anfangsbuchstaben abgelegt: <xsl:key name="myKey2" match="hobby" use="substring(.,1,1)"/> Die XPath Funktion key() gibt dann die entsprechenden Elemente zurück. <xsl:apply-templates select="key('myKey1','1912')"/> gibt alle name-Knoten mit Geburtstjahr 1912 zurück. <xsl:apply-templates select="key('myKey2','p')"/> gibt alle hobby-Knoten mit Anfangsbuchstabe "p" zurück. 91 Ein Beispiel <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <-- indexieren aller Personennamen nach Datum --> <xsl:key name="nameKey" match="name" use="../*/@date"/> <-- indexieren aller Hobbys nach Anfangsbuchstaben --> <xsl:key name="hobbyKey" match="hobby" use="substring(.,1,1)"/> <-- indexieren aller Adressen nach ihrer id --> <xsl:key name="addrKey" match="address" use="@id"/> <xsl:template match="people"> <html> <body> <!-- Geburtsjahr und Ort, absteigend sortiert auflisten --> <ul> <xsl:for-each select="//*[@date]"> <xsl:sort select="@date" order="descending"/> <li> <xsl:value-of select="key('nameKey', @date)/firstName"/> <xsl:text> </xsl:text> <xsl:value-of select="key('nameKey', @date)/lastName"/> <xsl:if test="key('addrKey', @address)"> <xsl:text>, </xsl:text> <xsl:value-of select="name()"/> in <!-- zum Datum die entsprechende Adresse ausgeben. --> <xsl:value-of select="key('addrKey', @address)/country"/> <xsl:text> </xsl:text> <!-- und das Datum selber --> <xsl:value-of select="@date"/> </xsl:if> </li> </xsl:for-each> </ul> <!-- alle Hobbies die mit 'p' anfangen auflisten --> <p> <xsl:value-of select="key('hobbyKey','p')" separator=", "/> </p> </body> </html> </xsl:template> </xsl:stylesheet> 92 xsl:function Beispiele von selber definierten XSL Funktionen <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:cs="http://www.isbe.ch/catalogFunctions" xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xsl:function name="cs:substring"> <xsl:param name="str" as="xs:string"/> <xsl:param name="n" as="xs:integer"/> <xsl:sequence select= "if( string-length($str) &gt;=$n ) then substring($str,1,$n) else error() "/> </xsl:function> <xsl:function name="cs:numberOfChildren"> <xsl:param name="node"/> <xsl:sequence select="count($node/*)"/> </xsl:function> ... Solche selber definierten Funktionen können dann folgendermassen benutzt werden: <xsl:value-of select="cs:numberOfChildren(/people)"/> 93 11 XSL-FO Einführung XSL-FO ... bildet zusammen mit XSLT und XPath die XSL (eXtensible Stylesheet Language) ... ist eine XML Applikation ... definiert das visuelle Layout-Modell ... wird mit Hilfe von einem Transformations-Tool (FOP, XEP, ...) nach PDF, PostScript, ... transformiert Dabei werden normalerweise keine FO-Dokumente direkt geschrieben, sondern die XML Daten werden mit Hilfe eines Stylesheets nach XSL-FO übersetzt, und dann mit Hilfe eines FO-Transformations-Tools nach Postscript, PDF, ... transformiert. 11.1.1 Ein einfaches Beispiel <?xml version="1.0"?> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="only" page-width="5.5cm" margin-left="1cm"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="only"> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="18pt"> A small <fo:external-graphic src="fo.jpg"/> example </fo:block> </fo:flow> </fo:page-sequence> </fo:root> 11.2 Die wichtigsten FO Elemente Es gibt 56 XSL-FO Elemente fo:block, fo:block-container, fo:character, fo:external-graphic, fo:flow, fo:footnote, fo:inline, fo:layout-master-set, fo:leader, fo:list-block, fo:list-item, fo:list-item-body, fo:list-item-label, fo:page-number, fo:page-sequence, fo:region-after, fo:region-before, fo:region-body, fo:root, fo:simple-page-master, fo:table-and-caption, fo:table, fo:tablebody, fo:table-caption, fo:table-cell, fo:table-column, fo:table-footer, fo:table-header, fo:table-row, . . . Die Elemente von XSL-FO definieren die Struktur oder den Aufbau (Unterteilung in Abschnitte, Blöcke, TextFelder, Bilder, ...) des Ausgabe- Dokuments. Die Attribute definieren das (lokale) Aussehen. Das Root Element XSL-FO Dokumente sind XML Dokumente. Darum beginnen FO-Dokumente mit der XML-Deklaration: <?xml version="1.0"?> XSL-FO Dokumente haben als Root-Element einen fo:root Knoten, in welchem der fo Namespace deklariert werden muss: xmlns:fo="http://www.w3.org/1999/XSL/Format" 95 Das Layout einer Seite Eine Seite ist aufgeteilt in die fünf Bereiche region-before, region-after, region-start, region-end und regionbody. Diese sehen in der üblichen Lesart (von links nach rechts, von oben nach unten) wie folgt aus: Normalerweise sind die Randbereiche (region-before, region-after, ...) reserviert für statische (wiederkehrende) Ausgaben (Logos, Adresse, Seitenzahl, ...), während der eigentliche Inhalt der Seite im Zentralbereich (body) ausgegeben wird. Die einzelnen Bereiche können dann weiter aufgeteilt werden in Blöcke (zum Beispiel durch fo:block, fo:table, fo:list-block, ...). Das Layout Master Set Das layout-master-set enthält alle Seiten-Vorlagen für die verschiedenen Seiten-Arten wie Titelseiten, linke und rechte Seiten, ... <fo:layout-master-set> <fo:simple-page-master master-name="first" page-height="297mm" page-width="210mm" margin-top="15mm" margin-bottom="15mm" margin-left="20mm" margin-right ="15mm"> <fo:region-body margin-top="20mm"/> <fo:region-before extent="15mm" region-name="fBefore"/> </fo:simple-page-master> <fo:simple-page-master master-name="odd" . . . </...> </fo:layout-master-set> Die einzelnen Seiten-Vorlagen sind dann verpackt in verschiedene page-master Elemente. Das layout-master-set Element hat keine Attribute, ist immer ein Kind-Knoten vom root-Knoten und hat als (einzig mögliche) Kinder einen (oder mehrere) simple-page-master oder page-sequence-master Knoten. Mit dem simple-page-master Knoten werden die Seitenränder (margin) und ev. die Papiergrösse festgelegt. Weiter wird definiert, in wie viele Bereiche (regions) die Seite aufgeteilt werden soll (region-body, regionbefore, region-after, ...) . Die normalen A4 Seitengrösse ist page-height="29.7cm" und page-width="21cm" Pro Seitentyp (Titelseite, Index-Seite, Inhaltsverzeichnis, ...) wird je eine separate Seiten-Vorlage definiert. 96 Die Platz-Verteilung für die Regionen <fo:root xmlns:fo= "http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="simple" page-height="10cm" page-width="9cm" margin-top="1.5cm" margin-bottom="1.5cm" margin-left="1cm" margin-right="1cm"> <fo:region-body margin-top="2cm" margin-bottom="1.5cm" background-color="#EEEEEE" /> <fo:region-before extent="1cm" background-color="yellow"/> <fo:region-after extent="1cm" background-color="yellow"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="simple"> <fo:static-content flow-name="xsl-region-before"> <fo:block font-size="12pt"> Hier ist der Platz für das Region- Before </fo:block> </fo:static-content> <fo:static-content flow-name="xsl-region-after" > <fo:block font-size="12pt"> Region-After ist oberhalb vom unteren Rand </fo:block> </fo:static-content> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="20pt" padding="0.5cm"> Region Body Region Body Region Body ... </fo:block> </fo:flow> </fo:page-sequence> </fo:root> 97 Definieren und Benutzen von verschieden Seiten-Typen <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <!-- Definition der ersten Seite--> <fo:simple-page-master master-name="erste" margin-left="5cm" margin-right="5cm“ margin-top="2cm" margin-bottom="3cm"> <fo:region-body margin-top="3cm" margin-bottom="2cm"/> <fo:region-before extent="3cm" region-name="first-before"/> <fo:region-after extent="1.5cm" region-name="first-after"/> </fo:simple-page-master> <!-- Definition der restlichen Seiten--> <fo:simple-page-master margin-bottom="3cm" margin-left="5cm" margin-right="5cm“ margin-top="5cm" master-name="restliche"> <fo:region-body margin-top="2.5cm" margin-bottom="2cm"/> <fo:region-before extent="2.5cm" region-name="rest-before"/> <fo:region-after extent="1.5cm" region-name="rest-after"/> </fo:simple-page-master> <!-- Wann soll welche Definition benutzt werden --> <fo:page-sequence-master master-name="global"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference master-reference="erste" page-position="first"/> <fo:conditional-page-master-reference master-reference="restliche" page-position="rest"/> <!-- default --> <fo:conditional-page-master-reference master-reference="restliche"/> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master> </fo:layout-master-set> <!-- Inhalt der verschiedenen Seiten --> <fo:page-sequence initial-page-number="1" master-reference="global"> <fo:static-content flow-name="first-before"> <fo:block> Hier ist der Titel der ersten Seite </fo:block> </fo:static-content> <fo:static-content flow-name="rest-before"> <fo:block> Der Titel der restlichen Seiten </fo:block> </fo:static-content> <fo:static-content flow-name="first-after"> <fo:block> Hier ist die Fusszeile der ersten Seite </fo:block> </fo:static-content> <fo:static-content flow-name="rest-after"> <fo:block> Hier ist die Fusszeile der restlichen Seiten </fo:block> </fo:static-content> <fo:flow flow-name="xsl-region-body"> ... hier kommt der Dokument Inhalt ... </fo:flow> </fo:page-sequence> </fo:root> 98 Der Dokument Inhalt: fo:flow fo:flow ist ein Kind-Knoten von fo:page-sequence und enthält eine beliebige Anzahl von Blöcken mit Text, Tabellen, Listen und Bildern. Mit Hilfe des flow Elementes wird der Dokument-Inhalt (der Fliesstext) definiert. fo:flow hat als mögliche Kind-Elemente: • fo:block (neuer Abschnitt) • block-container (neuer Abschnitt an fixem Ort) • table, table-and-caption (Tabelle) • list-block (Listen) Ein Beispiel <fo:flow flow-name="xsl-region-body"> <fo:block font-size="16pt"> This is the document content. This is the <fo:external-graphic src="cup.gif"/> document content. This is the... </fo:block> </fo:flow> Die statischen Bereiche: fo:static-content In den Randbereichen befindet sich normalerweise wiederkehrende Information (Überschriften, Fussnoten, Seitenzahlen, ...) <fo:static-content flow-name="xsl-region-after"> <fo:block text-align="end" font-size="10pt"> Seite <fo:page-number/> </fo:block> </fo:static-content> Abschnitte, Paragraphen, ... fo:block fo:block erzeugt einen Bereich (ein oder mehrere Zeilen lang. fo:block wird benutzt, um zusammengehörende Ausgabe-Elemente (ein Textabschnitt, eine Titelzeile, eine Tabelle, ...) zu gruppieren. <fo:block font-size="36pt"> Chapter 1: Introduction </fo:block> 99 Fest positionierte Felder: fo:block-container Ein fo:block-container Element kann an beliebiger Stelle auf des Seite positioniert werden <fo:block-container border-color="black" border-style="solid" border-width="1pt" height="1cm" width="4cm" padding="5pt" top="9.5cm" left="12cm" position="absolute" > <fo:block text-align="start" line-height="10pt" font-family="sans-serif" font-size="10pt"> Hier ist ein Text </fo:block> </fo:block-container> Format Änderungen in Zeilen: fo:inline Während mit fo:block ein neuer Abschnitt (eine neue Zeile) anfängt, kann fo:inline benutzt werden für Format-Änderungen innerhalb eines Blockes (einer Zeile). Typischerweise werden deshalb Blöcke entweder durch fo:block oder durch fo:inline Elemente unterteilt, je nachdem ob ein neuer Absatz erwünscht wird oder nicht. Einbinden von Bildern: fo:external-graphic Mit Hilfe von fo:external-graphic wird ein Bild in ein Dokument eingebettet. Mögliche Bildformate sind GIF und JPEG, ... <fo:block> Look at this! <fo:external-graphic src="cup.jpg"/> </fo:block> 100 Erstellen von Listen, Aufzählungen, ... Jedes fo:list-item Element enthält ein fo:list-item-label und ein fo:list-item-body Element. Ohne das start-indent Attribut im fo:list-item-body fängt der Inhalt der Auflistung am linken Rand an, und überschreibt das eventuell vorhandene Aufzählungs-Zeichen. <fo:list-block> <fo:list-item> <fo:list-item-label> <fo:block>a)</fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start()"> <fo:block>Dies ist das erste ....</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item> <fo:list-item-label> <fo:block>&#183;</fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start()"> <fo:block>Dies ist das zweite ....</fo:block> </fo:list-item-body> </fo:list-item> ... </fo:list-block> 101 11.2.1 Erzeugen von Tabellen Zum Erzeugen von Tabellen gibt es die Elemente: fo:table, fo:table-body, fo:table-cell, fo:table-column, fo:table-footer, fo:table-header, fo:table-row fo:table enhält (optional) einen table-header und/oder table-footer sowie einen table-body. Der table-body besteht dann aus table-row Elementen, welche wiederum aus table-cell Knoten zusammengesetzt sind. Durch die table-column Elemente wird bestimmt, wie viele und wie breit die einzelnen Spalten werden sollen. Die Breite kann auch Proportional zur Gesamt-Breite des Dokuments definiert werden (zum Beispiel 25%): column-width="proportional-column-width(25)" table-header und table-body enthalten dann pro Zeile eine table-row, welche pro Spalte eine table-cell enthält. Falls die Tabelle einen Rahmen erhalten soll, kann dies entweder in fo:table (Rahmen um ganze Tabelle), in table-column (Rahmen um eine Spalte), in table-row (Rahmen um eine Zeile) oder in fo:table-cell (Rahmen um einzelne Zelle) angegeben werden. Das Attribut padding gibt den Abstand vom Inhalt der Zelle (Text) und dem Rand der Zelle an. 102 Beispiel einer Tabelle <fo:table background-color="#EEEEEE" padding="3pt"> <fo:table-column column-width="20mm"/> <!--Definition der Spalten-Breiten --> <fo:table-column column-width="30mm"/> <fo:table-header font-weight="bold" background-color="#CCCCCC> <fo:table-row> <fo:table-cell border="2pt solid black" padding="3pt"> <fo:block> HTML </fo:block> </fo:table-cell> <fo:table-cell border="2pt solid black" padding="3pt"> <fo:block> XML-FO </fo:block> </fo:table-cell> </fo:table-row> </fo:table-header> <fo:table-body> <fo:table-row> <fo:table-cell border="1pt solid black" padding="3pt"> <fo:block> THEAD </fo:block> </fo:table-cell> <fo:table-cell border="1pt solid black" padding="3pt"> . . . . </fo:table-cell> </fo:table-row> </fo:table-body> </fo:table> 103 11.3 Die wichtigsten Attribute 11.3.1 Attribute sind Properties Die Attribute der FO-Elemente sind Formatierungs-Anweisungen. Sie bestimmen das Aussehen des Inhalts. Es gibt mehr als 200 Format Properties (Attribute), welche oft in verschiedensten Elementen angegeben werden können. Properties werden von aussen nach innen abgearbeitet. Bei sich widersprechenden Angaben, überschreibt die lokale Angabe die globale. Eine Property, welche an verschiedenen Orten benutzt werden kann, hat immer dieselbe Bedeutung. • • • • 11.3.2 Character Properties Alle XSL-FO Elemente, welche Text enthalten können (fo:block, fo:inline, ...), erlauben Attribute für das Setzen von Character Properties(color, font-size, font-family, text-decoration, ...). <fo:block font-family="Helvetica" font-size="12pt"> Hier ist ein <fo:inline text-decoration="underline"> wichtiger </fo:inline> <fo:inline color="red" font-weight="900"> Text </fo:inline> </fo:block> 11.3.3 Character Property: Farbe Die color-Property setzt die Schriftfarbe: <fo:inline color="green"> Text </fo:inline> Wie in CSS gibt es 16 vordefinierte Farben: Weitere Farben können als hexadezimale RGB-Trippel definiert werden: #RRGGBB. 104 11.3.4 Character Property: Font Die Property font-family definiert den zu benutzenden Font: <fo:inline font-family="Times Roman" > Times Roman </fo:inline> Zur Verfügung stehen unter anderem die Schriften Serif, Sans Serif, Times Roman, Courier und Symbol. Es können als fall-back auch mehrere font-family Werte angegeben werden. font-family=”Arial, Helvetica, sans-serif” Ausserdem können mit FOP alle Systemfonts eingebettet werden. 11.3.5 Character Property: Schriftgrösse Die Property font-size definiert die zu benutzende Schriftgrösse: <fo:inline font-family="Helvetica" ein Text in 24 Punkt Schrift </fo:inline> 11.3.6 font-size="24pt"> Character Property: Schrift Style Der Style des Fonts wird definiert durch die Property font-style <fo:block font-family="Times Roman" Times Roman italic </fo:block> font-style="italic"> Es gibt die Styles italic, normal, oblique, reverse-normal und reverse-oblique. 11.3.7 Character Property: Schrift-Dicke Die Dicke der Schrift wird definiert durch die Property font-weight: <fo:block font-family="Times Roman" font-weight="bold"> Hier ist ein bold Text </fo:block> <fo:block font-family="Times Roman" font-weight="normal"> Hier ist ein normaler Text </fo:block> 105 11.3.8 Character Property: Schrift-Dekorationen Unabhängig vom gewählten Font (Style, Farbe,...) können verschiedene Text Properties gesetzt werden. Die Property text-transform definiert, ob der ganze Text in Grossbuchstaben (uppercase), Kleinbuchstaben (lowercase) oder bloss die Anfangsbuchstaben in gross gesetzt werden sollen (capitalize). Die Property texttransform ist in FOP (noch) nicht implementiert. Die Property score-spaces definiert, ob im unter- (über/durch)-gestrichenen Text auch die Spaces unter (über/durch)-strichen sein sollen oder nicht. text-decoration="underline" --> unterstrichen text-decoration="overline" --> überstrichen text-decoration="line-through" --> durchgestrichen text-shadow="gray" --> Schattenschrift text-transform="capitalize" --> Alle Ersten Buchstaben Gross text-transform="uppercase" --> GROSSSCHRIFT text-transform="lowercase" --> kleinschrift Ein Beispiel: <fo:block text-align="start" line-height="12pt" font-family="sans-serif" font-size="10pt" text-decoration="underline" color="blue"> Ein blauer, unterstrichener sans-serif Text in 10pt Schrift. </fo:block> 11.3.9 Sentence Properties Sentence Properties bestimmen den Platz zwischen den einzelnen Buchstaben (letter-spacing), zwischen einzelnen Wörtern im Text (word-spacing) und zwischen den einzelnen Linien im Text (line-height, text-depth, text-altitude, ...), die Ausrichtung des Texts (text-align), ... <fo:block font-family="Helvetica" font-size="10pt" text-align="center" line-height="11pt"> Hier ist ein zentrierter Text. Hier ist ein zentrierter Text. Hier ist ein zentrierter Text. <fo:leader leader-pattern="rule" leader-length="6cm"/> <!- - Linie - -> </fo:block> <fo:block font-family="Helvetica" font-size="10pt" text-align="justify" letter-spacing="1pt"> Hier ist ein gesperrter . . . Hier ist ein Text. Hier ist noch ein Text. <fo:leader leader-pattern="rule" leader-length="6cm"/> <!- - Linie - -> </fo:block> 106 107 11.3.10 Sentence Property: Zeilenabstand Der Zeilenabstand im Text kann kontrolliert werden durch die Properties line-height, text-depth und textaltitude. <fo:block font-size="10pt" line-height="16pt" ...> Hier ist ein Text, ... </fo:block> Die Sentence Property line-height-shift-adjustment legt fest, ob subscripts und superscripts den LinienAbstand vergrössern sollen oder nicht. Durch text-depth (text-altitude) wird zusätzlicher Abstand nach (vor) jeder Zeile eingefügt. <fo:block font-family="Helvetica" font-size="10pt" text-align="left" text-depth="3pt" text-altitude="3pt" line-height="16pt"> Hier ist ein Text. Hier ist ein Text. Hier ist ein Text. </fo:block> 11.3.11 Sentence Property: Text Ausrichtung Ob der Text linksbündig, rechtsbündig, zentriert oder im Blocksatz gesetzt werden soll, wird durch die Property textalign gesetzt. Diese kann die folgenden Werte annehmen: start oder left --> linksbündig center --> zentriert end oder right --> rechtsbündig justify --> Blocksatz <fo:flow flow-name="xsl-region-body" > <fo:block font-size="12pt" text-align="start"> Hier ist ein linksbündiger Text, der . . . </fo:block> <fo:block> <fo:leader leader-length="7cm" leader-pattern="rule"/> </fo:block> <fo:block font-size="12pt" text-align="center" line-height="20pt"> Hier ist ein zentrierter Text mit grossem . . . </fo:block> <fo:block> <fo:leader leader-length="7cm" leader-pattern="rule"/> </fo:block> <fo:block font-size="12pt" text-align="end"> Hier ist ein rechtsbündiger Text . . . </fo:block> <fo:block> <fo:leader leader-length="7cm" leader-pattern="rule"/> </fo:block> <fo:block font-size="12pt" text-align="justify"> Hier ist ein Blocksatz Text, der . . . </fo:block> </fo:flow> 108 109 11.3.12 Attribut Listen Zum Erlangen eines konsistenten Layouts sollten für die verschiedenen Text-Bereiche (Fliesstext, Header, Footer, Tabellen, Listen) jeweils entsprechende Styles definiert werden. Hier zum Beispiel die Definition des text-style für den Fliesstext (pageMaster.xsl): <xsl:attribute-set name="text-style"> <xsl:attribute name="font-family">Arial, Helvetica, sans-serif</xsl:attribute> <xsl:attribute name="font-size">12pt</xsl:attribute> <xsl:attribute name="font-weight">normal</xsl:attribute> <xsl:attribute name="line-height">15pt</xsl:attribute> <xsl:attribute name="text-align">left</xsl:attribute> </xsl:attribute-set> Dieser wird dann wie folgt im Stylesheet benutzt (makeFO.xsl): <fo:flow flow-name="xsl-region-body"> <fo:block xsl:use-attribute-sets="text-style"> <xsl:apply-templates select="doc"/> </fo:block> </fo:flow> Analog sollte für Tabellen, Listen, ... vorgegangen werden. 11.3.13 Für die Übung: Einstellungen in oXygen für XSL-FO Beim Ankreuzen von „FO Transformation ausführen“ wird das xml-Dokument mit Hilfe des Stylesheets nach XSL-FO transformiert und das erzeugte fo-File danach mit FOP nach pdf umgewandelt. 110