XML-Verarbeitung in Java
Transcription
XML-Verarbeitung in Java
Content .. ....... ... ..... ... ... .. ... ... ... ... ... ... ... ... ... . ... .. ... ... . . ... . ... ... ... ... . ... ... ... ... ... . ... ... ... ... . ... ... ... ... ... . ... ... ... ... . ... ... ... ... ... . ... ... ... ... . ... .. Information Concept Topic XML-Verarbeitung in Java Praktikum zur Vorlesung Intelligente Informationssysteme WS 2004/05 Gunar Fiedler ([email protected]) Question1 : When should I use XML? Answer: When you need a buzzword in your resume. 1 XML Die EXtensible Markup Language ist eine Sprache zur Beschreibung von Daten. Dabei bedient man sich einer Menge von Markierungselementen, den sogenannten Tags. Anders als bei HTML ist die Menge der Tags nicht vorbestimmt, jeder kann sich seine eigenen Tags definieren. Die Syntax eines XML-Dokuments ist einfach: <?xml version="1.0" encoding="ISO-8859-1"?> <tour> <artist>R.E.M.</artist> <title>Tour 2005</title> <event> <place name="ColorLine Arena"> <address>Sylvesterallee, 22525 Hamburg</address> </place> <date day="17" month="2" year="2005" hour="20" minute="0" /> </event> </tour> Jedes Dokument beginnt mit der XML declaration, welche die XML-Version und die Zeichencodierung der Datei angibt: <?xml version="1.0" encoding="ISO-8859-1"?> Momentan wird immer XML-Version 1.0 verwendet, ISO-8859-1 bezeichnet den Zeichensatz Latin 1/West European. 1 www.w3schools.com/xml/xml_whatis.asp 1 XML XML-Verarbeitung <tour> Das erste Tag bezeichnet das Wurzelelement des Dokuments, in das alle anderen Elemente eingebettet sind. Jedes XML-Dokument hat genau 1 Wurzelelement. Im Unterschied zu HTML wird in XML bei der Definition von Bezeichnern zwischen Groß- und Kleinschreibung unterschieden. Da jeder seine eigenen Tags definieren kann, würde bald großes Chaos herrschen, und niemand könnte unterscheiden, wer ein bestimmtes Tag mit welcher Bedeutung definiert hat. Aus diesem Grund werden Tagnamen in Namensräume eingeteilt. Ein Namensraum ist ein weltweit eindeutiger Bezeichner, also eine URI2 . Dieser URI wird im Wurzelelement ein symbolischer Bezeichner zugewiesen: <tour xmlns="http://www.is.informatik.uni-kiel.de/events" xmlns:h="http://www.w3.org/1999/xhtml"> <artist><h:h1>R.E.M.</h:h1></artist> </tour> Namespaces werden durch xmlns:XX-Attribute festgelegt, wobei XX dem symbolischen Bezeichner entspricht. Dem eigentlichen Tag-Namen wird dieser Bezeichner vorangestellt, im Beispiel wurde aus dem HTML-Tag h1 ein Tag h:h1, da der XHTML-Namespace, in dem h1 definiert ist, mit dem symbolischen Bezeichner h versehen wurde. Tags ohne explizite Namespace-Angabe werden dem StandardNamespace zugeordnet. Dessen Definition erfolgt durch das xmlns-Attribut des Wurzelknotens. <artist>R.E.M.</artist> Das Element artist wird Kindelement des ihm übergeordneten Elements tour genannt. Analog sind title und event Kinder von tour; place und date sind Kinder von event. Jedes öffnende“ Tag (<tour>, <artist>, <date>, ...) muss wieder ” geschlossen werden. Dies geschieht durch Angabe des Tag-Namens mit einem vorangestellten Schrägstrich (/), z.B. </tour>. Besitzt das Element keine Kindelemente, wie z.B. das Element date, so kann der Schrägstrich auch vor die schließende Klammer des öffnenden Tags geschrieben werden (<date />), diese Schreibweise ist zu <date></date> äquivalent. Ein Kind-Element muss sich vollständig innerhalb des Eltern-Elements befinden, d.h. es wird nach dem öffnenden Tag des Eltern-Elements definiert und vor dem schließenden Tag des Eltern-Elements geschlossen. Durch diese Konstruktionsvorschrift bilden die Elemente eines XML-Dokuments einen Baum, dessen Wurzelknoten gerade das Wurzelelement des Dokuments ist. Besitzt ein XML-Element mehrere Kinder, so bilden die Kind-Elemente eine Liste und keine Menge oder Multimenge. <date day="17" month="2" year="2005" hour="20" minute="0" /> 2 2 wobei es sich um einen URN und nicht um eine URL handelt WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 2 XML-Verarbeitung Jedes Element kann Attribute haben. Attribute besitzen einen Namen und einen Wert aus dem Wertebereich eines atomaren Datentyps, z.B. eine Zeichenkette oder eine Zahl. Attribute werden innerhalb der Klammern des öffnenden Tags eines Elements durch die Angabe name="value" definiert. Der Wert des Attributs muss stets in Hochkommata eingeschlossen werden. Ein Element kann beliebig viele Attribute besitzen. Auch Attribute werden in Namespaces eingeteilt. <artist>R.E.M.</artist> Jedes Element kann Text beinhalten. Es ist möglich, Kind-Elemente und TextAbschnitte zu mischen. Falls der Text eines Elements selbst Tags enthält, die aber nicht interpretiert werden sollen (wie <pre>-Abschnitte in HTML bzw. die verbatimUmgebung in LATEX), so muss er in eine CDATA-Section eingeschlossen werden: <event> <!-- ... --> <travelinfo> <![CDATA[ <strong>PKW</strong><br />Fahren Sie ...]]> </travelinfo> </event> Neben den vorgestellten Strukturierungselementen gibt es noch zwei weitere: Kommentare und Processing Instructions. Ein Kommentar wird durch die Markierungen <!-- und --> eingeschlossen: <!-- Das ist ein Kommentar --> Eine Processing Instruction ist, wie der Name schon sagt, eine Verarbeitungsanweisung für die Anwendung, welche das XML-Dokument liest und verarbeitet. Sie wird in <? und ?> Markierungen eingeschlossen. Eine wichtige Processing Instruction ist z.B. die Angabe eines fest zugeordneten Stylesheets (siehe auch Abschnitt 4.1): <?xml-stylesheet type="text/xsl" href="renderAsHTML.xsl"?> Ein XML-Dokument heißt well formed“, wenn es die dargestellte Syntax einhält. ” Falls es zusätzlich einem gegebenen Schema bzw. einer DTD3 entspricht, nennt man es valid“. ” 2 XML-Verarbeitung XML-Dokumente sind zunächst einmal reine Textdateien. Um mit den Informationen in diesen Dateien arbeiten zu können, muss ein Programm den Inhalt der Datei 3 Document Type Definition, eine alternative Möglichkeit, die Struktur festzulegen, siehe auch Abschnitt 3 WS 2004/05, Gunar Fiedler, ISE@CAU 3 2 XML-Verarbeitung XML-Verarbeitung analysieren und die kodierten Informationen extrahieren. Vor dieser Aufgabe steht jedes Programm, das XML-Daten verarbeitet, deshalb existieren diverse Laufzeitbibliotheken, die diese Schritte unterstützen. Für die Verarbeitung von XML-Daten haben sich zwei wesentliche Ansätze durchgesetzt: SAX und DOM. Die Simple API for XML“ (SAX) stellt eine API bereit, ” mit deren Hilfe das Programm während des Parsens der Datei auf gefundene Token (öffnende und schließende Tags, Textelemente, ...) reagieren kann. Im Gegensatz dazu stellt das Document Object Model“ (DOM) eine objektorientierte Datenstruk” tur bereit, die das Dokument als Baum darstellt und Funktionen zum Traversieren anbietet. SAX und DOM sind sprachunabhängige Spezifikationen, es existieren Implementierungen für alle in der Praxis eingesetzten Programmiersprachen. Wir konzentrieren uns hier auf die Java API for XML und die Bibliothek Xerces“. ” 2.1 Einlesen von XML-Dokumenten mittels SAX SAX bietet dem Programmierer ein ereignisgesteuertes Zugriffsmodell auf XMLDatenströme. Ein Parser analysiert das XML-Dokument und erzeugt bei Auftreten eines bestimmten Tokens ein Ereignis, auf welches das Programm reagiert. Folgende Ereignisse werden durch den SAX-Parser erzeugt: • startDocument: Beginn des Parsens eines Dokuments • endDocument: Ende des Parsens eines Dokuments • startElement(namespace, name, qname, attrs): Ein öffnendes Tag wurde gefunden. Der Parser teilt dem Programm den Namespace, den Namen, den qualifizierten Namen4 und die Attributliste mit. • endElement(namespace, name, qname): ein schließendes Tag wurde gefunden • characters(buf[], offset, len): Liefert len Zeichen im Puffer buf[] an der Stelle offset • ... analoge Ereignisse für die restlichen Elemente 2.1.1 Das Grundgerüst eines SAX-basierten Programms Der SAX-Parser braucht ein Handler-Objekt, an das er seine Ereignisse schicken kann. Die Klasse dieses Objekts muss das ContentHandler-Interface implementieren. Dieses Interface stellt zu den Ereignissen korrespondierende Methoden bereit. Die Klasse DefaultHandler entspricht diesen Anforderungen, sie implementiert leere Methoden für alle SAX-Ereignisse. Man kann seine eigenen Handler-Klassen von DefaultHandler ableiten: 4 4 Die Kombination aus Namespace und Namen, falls der Parser Namespaces nicht bearbeitet, sind die Parameter namespace und name leer. WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 2 XML-Verarbeitung p u b l i c c l a s s MyHandler extends D e f a u l t H a n d l e r { /∗ . . . ∗/ } Als nächstes müssen die Methoden für die gewünschten Ereignisse überschrieben werden. Angenommen, wir wollen unser Beispiel-XML-Dokument bearbeiten und die Namen aller Veranstaltungsorte ausgeben. Wir müssen also das Ereignis startElement abfangen und das name-Attribut ausgeben: p u b l i c c l a s s MyHandler extends D e f a u l t H a n d l e r { p u b l i c v o i d s t a r t E l e m e n t ( S t r i n g namespaceURI , S t r i n g sName , S t r i n g qName , Attributes attrs ) throws SAXException { // Element−Name S t r i n g eName = sName ; // F a l l s Namespaces n i c h t v e r a r b e i t e t werden , nehmen w i r qName i f ( ”” . e q u a l s ( eName ) ) eName = qName ; i f ( ( ”p l a c e ” . e q u a l s ( eName ) && ( n u l l != a t t r s ) ) { // name−A t t r i b u t s u c h e n f o r ( i n t i = 0 ; i < a t t r s . g e t L e n g t h ( ) ; i ++) { S t r i n g aName = a t t r s . getLocalName ( i ) ; i f ( ”” . e q u a l s ( aName ) ) aName = a t t r s . getQName ( i ) ; i f ( ”name ” . e q u a l s ( aName ) ) { // Ausgabe d e s Wertes System . o u t . p r i n t l n ( a t t r s . g e t V a l u e ( i ) ) ; } } } } } Auch die anderen SAX-Ereignisse werden durch Überschreiben der jeweils korrespondierenden Methoden behandelt, nähere Hinweise sind in der Dokumentation der API des Java Web Service Development Packs“ unter ContentHandler und ” DefaultHandler zu finden. WS 2004/05, Gunar Fiedler, ISE@CAU 5 2 XML-Verarbeitung XML-Verarbeitung Soll ein XML-Dokument verarbeitet werden, müssen folgende Schritte unternommen werden: 1. Ein geeigneter Handler ist zu implementieren. 2. Ein Parser ist zu instanziieren. Es existieren verschiedene Parser-Implementierungen, jeweils mit eigenen Stärken und Schwächen. Die Instanziierung erfolgt nach dem Factory-Prinzip5 . 3. Durch den Aufruf der Methode parse wird der Vorgang gestartet. // D i e s e B i b l i o t h e k e n und K l a s s e n werden t y p i s c h e r w e i s e b e n o e t i g t import o r g . xml . s a x . ∗ ; import o r g . xml . s a x . h e l p e r s . D e f a u l t H a n d l e r ; import j a v a x . xml . p a r s e r s . S A X P a r s e r F a c t o r y ; import j a v a x . xml . p a r s e r s . P a r s e r C o n f i g u r a t i o n E x c e p t i o n ; import j a v a x . xml . p a r s e r s . SAXParser ; /∗ . . . ∗/ p u b l i c v o i d parseDocument ( F i l e f ) { try { // i n s t a n z i i e r e n d e s H a n d l e r s D e f a u l t H a n d l e r h a n d l e r = new MyHandler ( ) ; // F a c t o r y e r z e u g e n SAXParserFactory f a c t o r y = SAXParserFactory . newInstance ( ) ; // P a r s e r −I n s t a n z e r z e u g e n SAXParser p a r s e r = f a c t o r y . newSAXParser ( ) ; // p a r s e n m it unserem H a n d l e r parser . parse ( f , handler ) } catch ( E x c e p t i o n e ) { e . printStackTrace ( ) ; /∗ . . . ∗/ } } /∗ . . . ∗ 5 6 siehe Software-Pattern WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 2 XML-Verarbeitung 2.2 Verarbeiten von XML-Dokumenten mit DOM Im Gegensatz zum parsenden Ansatz von SAX ist DOM ein speicherinternes Format zum Bearbeiten von XML-Daten. DOM definiert eine Reihe von Interfaces, mit deren Hilfe man sich durch die Baumstruktur des Dokuments bewegen kann. 2.2.1 Die Elemente von DOM DOM stellt ein Dokument als eine Hierarchie von Node-Objekten dar. Je nach Knotentyp implementieren diese Objekte auch spezialisierte Interfaces. Die wichtigsten Interfaces sind: Document DocumentFragment Element Attr Text Comment repräsentiert repräsentiert repräsentiert repräsentiert repräsentiert repräsentiert ein XML-Dokument einen Teilwald“ des Dokumenten-Baums ” ein Element des XML-Dokuments ein Attribut eines Elements einen Textabschnitt einen Kommentar Diese Interfaces definieren Methoden zum Durchmustern der Hierarchie. Nähere Informationen zum Datenmodell findet man unter http://www.w3.org/DOM/DOMTR. 2.2.2 Die Arbeit mit DOM Die Arbeit mit DOM-Strukturen impliziert folgende Teilaufgaben: Einlesen / Erstellen eines Dokuments Es existieren zwei Wege, eine DOM-Struktur zu erzeugen: from scratch“ oder durch Einlesen bestehender XML-Daten. Beiden ” Methoden ist gemeinsam, dass ein Dokumenten-Baum durch einen sogenannten DocumentBuilder initialisiert wird: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Ein neues, leeres Dokument wird durch den Aufruf der Methode newDocument erzeugt: Document doc = builder.newDocument(); Ein bestehendes Dokument, z.B. eine XML-Datei oder ein XML-Datenstrom aus einer anderen beliebigen Quelle, kann über einen Parser in einen DOM-Baum überführt werden. Im Standardfall geschieht dies über den Aufruf der Methode parse: Document doc = builder.parse( new File("inputfile.xml")); Neben Objekten der Klasse File sind auch InputStreams, InputSources und URIs mögliche Datenquellen für den Parser. WS 2004/05, Gunar Fiedler, ISE@CAU 7 2 XML-Verarbeitung XML-Verarbeitung Traversieren und Verändern Die Knoten-Objekte eines DOM-Baums sind diesem Baum fest zugeordnet. Neue Knoten werden über die entsprechenden createXXXMethoden des Dokumentenobjekts erzeugt: Element tour = doc.createElement("tour"); Element artist = doc.createElement("artist"); Text artistValue = doc.createTextNode("R.E.M."); /* ... */ Für Attribute eines Elements existiert im Interface Element die setAttributeMethode: Element place = doc.createElement("place"); place.setAttribute("name","ColorLine Arena"); Nach ihrer Definition müssen die Elemente zu einem Baum zusammengefügt werden: artist.appendChild(artistValue); tour.appendChild(artist); doc.appendChild(tour); // das Wurzelelement Weiterhin stellen die DOM Interfaces Methoden zum Traversieren des Baumes zur Verfügung: getDocumentElement getAttribute getElementsByTagName getChildNodes getPreviousSibling getNextSibling getParentNode ... gibt den Wurzelelement des Baumes zurück gibt den Wert eines Attributs zurück liefert eine Liste aller Kindelemente mit einem bestimmten Tag-Namen gibt eine Liste aller Kindelemente zurück gibt den Knoten zurück, der den gleichen Vaterknoten hat und in der Liste der Kinder des Vaters vor dem Ausgangsknoten liegt analog gibt den Vaterknoten zurück Zum Verändern des Baumes stehen ebenfalls Methoden bereit: insertBefore setAttribute removeAttribute removeChild replaceChild importNode fügt einen neuen Knoten ein setzt ein Attribut eines Elements entfernt ein Attribut entfernt einen Knoten aus der Liste der Kinder ersetzt einen Knoten in der Liste der Kinder importiert einen Knoten aus einem anderen Dokument, der Knoten kann anschließend in die Struktur eingefügt werden ... 8 WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 2 XML-Verarbeitung Ausschreiben / Serialisieren Eine DOM-Hierarchie ist eine interne Speicherstruktur. Zum Abspeichern oder für den Datenaustausch ist es notwendig, das DOMDokument wieder in ein XML-Dokument, also eine Textdatei bzw. einen Zeichenstrom, umzuwandeln. Dies kann auf verschiedene Art und Weise geschehen; z.B. zu Fuß, indem man den Baum traversiert und entsprechende Ausgaben tätigt. Da diese Aufgabe für XML-verarbeitende Programme häufig anfällt, bietet die XercesBibliothek vorgefertigte Klassen, um den Prozess der Serialisierung zu automatisieren. Durch ein Objekt der Klasse XMLSerializer kann man sich aus einem DOMDokument einen XML-Datenstrom erzeugen lassen. Mit Hilfe der Klasse OutputFormat stellt man notwendige Format-Parameter ein: OutputFormat format = new OutputFormat(doc); format.setEncoding("UTF-8"); // Kodierung setzen format.setIndenting(true); // Einrückung aktivieren format.setIndent(2); // 2 Zeichen einrücken format.setLineWidth(75); // maximale Zeilenbreite FileOutputStream f = new FileOutputStream("outputfile.xml"); XMLSerializer out = new XMLSerializer(f,format); out.serialize(doc); f.close(); Notwendige Packages Um mit DOM zu arbeiten, werden typischerweise folgende Packages benötigt: javax.xml.parsers.* org.xml.sax.* java.io.* org.w3c.dom.* org.apache.xml.serialize.* DocumentBuilder, DocumentBuilderFactory, etc. parser-spezifische Klassen Ein- und Ausgabe die DOM Interfaces Serialisierung (Xerces-spezifisch) 2.2.3 Ein zusammenhängendes Beispiel Das folgende Listing zeigt ein Codefragment, das ein DOM-Dokument einliest, um einen Teilbaum erweitert und ausschreibt. Zu Begin habe die Datei diesen Inhalt: <?xml version="1.0" encoding="UTF-8"?> <tour> <artist>R.E.M.</artist> <title>Tour 2005</title> <event> <place name="ColorLine Arena"> <address>Sylvesterallee, 22525 Hamburg</address> </place> WS 2004/05, Gunar Fiedler, ISE@CAU 9 2 XML-Verarbeitung XML-Verarbeitung <date day="17" hour="20" minute="0" month="2" year="2005"/> </event> </tour> Das Listing: import import import import import j a v a x . xml . p a r s e r s . ∗ ; o r g . xml . s a x . ∗ ; java . io . ∗ ; o r g . w3c . dom . ∗ ; o r g . a p a c h e . xml . s e r i a l i z e . ∗ ; /∗ . . . ∗/ // E r z e u g e n d e s Dokuments DocumentBuilderFactory f a c t o r y = DocumentBuilderFactory . newInstance ( ) ; D o c u m e n t B u i l d e r b u i l d e r = f a c t o r y . ne w D o c um e ntBuilde r ( ) ; Document doc = b u i l d e r . newDocument ( ) ; // t o u r i s t d a s W u r z e l e l e m e n t E le m e nt t o u r = doc . getDocumentElement ( ) ; // E r z e u g e n d e r E l e m e n t e und E r s t e l l u n g d e s T e i l b a u m s E le m e nt e v e n t = doc . c r e a t e E l e m e n t ( ”e v e n t ” ) ; E le m e nt p l a c e = doc . c r e a t e E l e m e n t ( ”p l a c e ” ) ; p l a c e . s e t A t t r i b u t e ( ”name ” , ”Arena L e i p z i g ” ) ; E le m e nt a d d r e s s = doc . c r e a t e E l e m e n t ( ”a d d r e s s ” ) ; a d d r e s s . a p p e n d C h i l d ( doc . c r e a t e T e x t N o d e ( ”Am S p o r t f o r u m , 04105 L e i p z i g ” ) ) ; place . appendChild ( address ) ; event . appendChild ( place ) ; E le m e nt d a t e = doc . c r e a t e E l e m e n t ( ”d a t e ” ) ; d a t e . s e t A t t r i b u t e ( ”day ” , ”13 ” ) ; d a t e . s e t A t t r i b u t e ( ”month ” , ”2 ” ) ; d a t e . s e t A t t r i b u t e ( ”y e a r ” , ”2005 ” ) ; d a t e . s e t A t t r i b u t e ( ”h o u r ” , ”20 ” ) ; d a t e . s e t A t t r i b u t e ( ”m i n u t e ” , ”0 ” ) ; event . appendChild ( date ) ; // w i r f u e g e n d a s neue E l e m e n t v o r dem e r s t e n ”e v e n t ” e i n // f a l l s b i s h e r k e i n ”e v e n t ” e x i s t i e r t , w i r d d a s neue E r e i g n i s // am Ende d e r K i n d l i s t e e i n g e f u e g t N o d e L i s t e v e n t s = t o u r . getElementsByTagName ( ”e v e n t ” ) ; i f ( events . getLength () > 0 ) { tour . i n s e r t B e f o r e ( event , ev ent s . item ( 0 ) ) ; } else { tour . i n s e r t B e f o r e ( event , n u l l ) ; 10 WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 3 XML Schema } // A u s s c h r e i b e n d e r D a t e i OutputFormat f o r m a t = new OutputFormat ( doc ) ; f o r m a t . s e t E n c o d i n g ( ”UTF−8 ” ) ; format . s e t I n d e n t i n g ( true ) ; format . s e t I n d e n t ( 2 ) ; format . setLineWidth ( 7 5 ) ; format . setPreserveSpace ( f a l s e ) ; F i l e O u t p u t S t r e a m f o s = new F i l e O u t p u t S t r e a m ( ” o u t p u t f i l e . xml ” ) ; X M L S e r i a l i z e r o u t p u t = new X M L S e r i a l i z e r ( f o s , f o r m a t ) ; o u t p u t . s e r i a l i z e ( doc ) ; fos . close (); /∗ . . . ∗/ Die Datei outputfile.xml hat anschließend diesen Inhalt: <?xml version="1.0" encoding="UTF-8"?> <tour> <artist>R.E.M.</artist> <title>Tour 2005</title> <event> <place name="Arena Leipzig"> <address>Am Sportforum, 04105 Leipzig</address> </place> <date day="13" hour="20" minute="0" month="2" year="2005"/> </event> <event> <place name="ColorLine Arena"> <address>Sylvesterallee, 22525 Hamburg</address> </place> <date day="17" hour="20" minute="0" month="2" year="2005"/> </event> </tour> 3 XML Schema In den ersten Abschnitten haben wir XML allgemein als Datenbeschreibungssprache eingeführt. Damit ein Programm XML-Daten verarbeiten kann, muss es allerdings wissen, welche Tags an welcher Stelle welche Bedeutung haben. Zumindest für die Beschreibung der Syntax eines XML-Dokuments gibt es explizite Beschreibungsmittel: Document Type Defintions (DTDs) und XML-Schemata. Mit letzterem wollen wir uns hier beschäftigen. WS 2004/05, Gunar Fiedler, ISE@CAU 11 3 XML Schema XML-Verarbeitung Ein XML-Schema ist selbst wieder ein XML-Dokument, in dem festgehalten wird, welche Elemente auf welche Art und Weise innerhalb eines Instanz-Dokuments vorkommen dürfen. Der Tag-Name des Wurzelelements des Schema-Dokuments ist schema aus dem Namespace http://www.w3.org/2001/XMLSchema: <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.is.informatik.uni-kiel.de/events" xmlns="http://www.is.informatik.uni-kiel.de/events"> <!-- ... --> </xsd:schema> Mittels des Attributs targetNamespace wird der Namespace angegeben, dessen Elemente hier beschrieben werden. Mit Hilfe von xsd:element-Tags werden einzelne Elemente definiert. Es wird unterschieden zwischen einfachen und komplexen Typen. Ein Element mit einem einfachen Typ enthält genau einen Textwert aus dem Wertebereich des Typs, z.B. eine Zeichenkette oder eine Zahl. Elemente mit komplexem Typ enthalten Attribute, Unterelemente und / oder gemischten Inhalt. Für Unterelemente und Attribute lassen sich Kardinalitätsbedingungen und Standardwerte angeben. Um die Wiederverwendung von Schema-Elementen zu unterstützen, bietet XML-Schema diverse Mechanismen zur Definition und Verwaltung von Typen an. Eine Möglichkeit, das Schema unseres Beispieldokuments zu beschreiben, ist die folgende: <? xml v e r s i o n=”1 . 0 ” e n c o d i n g=”UTF−8 ”?> <x s d : s c h e m a x m l n s : x s d=” h t t p : //www . w3 . o r g /2001/XMLSchema ” t a r g e t N a m e s p a c e=” h t t p : //www. i s . i n f o r m a t i k . u n i − k i e l . de / e v e n t ” x m lns=” h t t p : //www. i s . i n f o r m a t i k . u n i − k i e l . de / e v e n t ”> <!−− e i n w i e d e r v e r w e n d b a r e r k o m p l e x e r Typ −−> <x s d : c o m p l e x T y p e name=”e v e n t T y p e ”> <!−− d i e E l e m e n t e s o l l e n i n d e r a n g e g e b e n e n R e i h e n f o l g e a u f t r e t e n −−> <x s d : s e q u e n c e> <!−− 1 . K i n d e l e m e n t : p l a c e −−> <x s d : e l e m e n t name=”p l a c e ”> <!−− e i n anonymer k o m p l e x e r Typ −−> <x s d : c o m p l e x T y p e> <x s d : s e q u e n c e> <!−− 1 . K i n d e l e m e n t von p l a c e : a d d r e s s −−> <x s d : e l e m e n t name=”a d d r e s s ” t y p e=” x s d : s t r i n g ” /> </ x s d : s e q u e n c e> <!−− A t t r i b u t : name −−> < x s d : a t t r i b u t e name=”name ” t y p e=” x s d : s t r i n g ” u s e=” r e q u i r e d ” /> 12 WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 3 XML Schema </ x s d : c o m p l e x T y p e> </ x s d : e l e m e n t> <!−− 2 . K i n d e l e m e n t : d a t e −−> <x s d : e l e m e n t name=”d a t e ”> <x s d : c o m p l e x T y p e> <!−− A t t r i b u t : day −−> < x s d : a t t r i b u t e name=”day ”> <x s d : s i m p l e T y p e> <!−− e i n e I n t e g e r −Z a h l von 1 b i s 31 −−> < x s d : r e s t r i c t i o n b a s e=” x s d : i n t e g e r ”> < x s d : m i n I n c l u s i v e v a l u e=”1 ” /> <x s d : m a x I n c l u s i v e v a l u e=”31 ” /> </ x s d : r e s t r i c t i o n> </ x s d : s i m p l e T y p e> </ x s d : a t t r i b u t e> <!−− A t t r i b u t month , w i r l a s s e n d i e E i n s c h r a e n k u n g d e r U e b e r s i c h t l i c h k e i t wegen weg −−> < x s d : a t t r i b u t e name=”month ” t y p e=” x s d : i n t e g e r ” /> <!−− A t t r i b u t e y e a r −−> < x s d : a t t r i b u t e name=”y e a r ” t y p e=” x s d : i n t e g e r ” /> <!−− A t t r i b u t h o u r −−> < x s d : a t t r i b u t e name=”h o u r ” t y p e=” x s d : i n t e g e r ” /> <!−− A t t r i b u t m i n u t e −−> < x s d : a t t r i b u t e name=”m i n u t e ” t y p e=” x s d : i n t e g e r ” /> </ x s d : c o m p l e x T y p e> </ x s d : e l e m e n t> </ x s d : s e q u e n c e> </ x s d : c o m p l e x T y p e> <!−− d a s W u r z e l e l e m e n t t o u r −−> <x s d : e l e m e n t name=”t o u r ”> <x s d : c o m p l e x T y p e> <x s d : s e q u e n c e> <!−− z u n a e c h s t e i n K i n d e l e m e n t a r t i s t −−> <x s d : e l e m e n t name=” a r t i s t ” t y p e=” x s d : s t r i n g ” /> <!−− danach e i n K i n d e l e m e n t t i t l e −−> <x s d : e l e m e n t name=” t i t l e ” t y p e=” x s d : s t r i n g ” minOccurs=”1 ” maxOccurs=”1 ” /> <!−− e i n e L i s t e von e v e n t −E l e m e n t e n −−> <x s d : e l e m e n t name=”e v e n t ” t y p e=”e v e n t T y p e ” minOccurs=”0 ” maxOccurs=”unbounded ” /> </ x s d : s e q u e n c e> </ x s d : c o m p l e x T y p e> </ x s d : e l e m e n t> </ x s d : s c h e m a> WS 2004/05, Gunar Fiedler, ISE@CAU 13 4 XSL Transformationen XML-Verarbeitung 4 XSL Transformationen Aufbauend auf dem XML-Standard ist die EXtensible Stylesheet Language eine Sprache, um Transformationen von XML-Dokumenten zu beschreiben. In sogenannten Stylesheets werden Regeln angegeben, wie ein XML-Dokument einer bestimmten Struktur in ein Dokument mit einer anderen Struktur überführt wird. Das AusgabeDokument ist entweder wieder ein XML- oder Text-Dokument, bei Nutzung von Formating Objects (FOs) kann aber z.B. auch ein PDF-Dokument gerendert werden. 4.1 Stylesheets Ein Stylesheet ist selbst eine XML-Datei, welche die Transformationsregeln beschreibt. Das Wurzelelement jedes Stylesheets ist ein stylesheet-Element aus dem Namespace http://www.w3.org/1999/XSL/Transform: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <!-- ... --> </xsl:stylesheet> Mit Hilfe des Elements xsl:output wird der Typ des Ausgabedokuments bestimmt, z.B. html oder xml. Im Anschluss können die Transformationsregeln definiert werden. Dies geschieht über Templates. Einem Template wird ein XPath-Ausdruck6 zugeordnet. Erfüllt ein zu verarbeitendes Element mit seinen Eigenschaften diesen Ausdruck, so wird das Template für dieses Element ausgeführt und erzeugt eine Ausgabe. Was ausgegeben werden soll, wird in den Rumpf des Templates geschrieben. Beispielsweise soll das Wurzelelement tour unseres Dokuments transformiert werden. Dafür definieren wir ein Template, das bei Auftreten von /tour - also tour direkt an der Wurzel - aufgerufen wird. <xsl:template match="/tour"> <html> <head><title>Veranstaltungskalender</title></head> <body> <h1> <xsl:apply-templates select="artist" />: <xsl:apply-templates select="title" /> </h1> 6 siehe Abschnitt 4.2 14 WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 4 XSL Transformationen <xsl:apply-templates select="event" /> </body> </html> </xsl:template> Die xsl-Elemente veranlassen den Stylesheet-Prozessor zu bestimmten Aktionen. Alles, was nicht dem xsl-Namespace entstammt, wird in die Ausgabe kopiert. Im obigen Beispiel sehen wir ein wichtiges xsl-Tag: xsl:apply-templates. Mit Hilfe dieses Tags werden kontext-abhängig weitere Templates aufgerufen. Das selectAttribut enthält einen XPath-Ausdruck, der relativ zum aktuellen Element die zu bearbeitenden Elemente auswählt. <xsl:template match="artist"> <xsl:value-of select "text()" /> </xsl:template> Mit Hilfe des xsl-Elements value-of kann der Wert eines Ausdrucks bestimmt werden. Dies kann z.B. ein Element, ein Attribut, der Aufruf einer xsl-Funktion, aber auch eine komplexere Berechnung sein. <xsl:value-of select="date/@day" />. Dieser Ausdruck gibt zum Beispiel den Wert des Attributs day des Unterelements date zurück. Attributnamen wird stets das @-Zeichen vorangestellt. XSL definiert eine Reihe von Tags zur Ablaufsteuerung, die wichtigsten sind in der nachfolgenden Tabelle zusammengestellt: <xsl:if test=""></xsl:if> <xsl:for-each select=""> <xsl:choose> <xsl:text> <xsl:comment> WS 2004/05, Gunar Fiedler, ISE@CAU wenn der Ausdruck im Attribut test wahr ist, wird der eingeschlossene Teil des XSL-Codes ausgeführt. <xsl:if> unterstützt kein else. Mit Hilfe von test="XPath-Ausdruck" lässt sich die Existenz eines Elements (oder Atributs) überprüfen: <xsl:if test="artist"> prüft, ob ein Unterelement artist existiert. iteriert über eine Menge von Elementen. analog zu switch- oder case-Statements wird ein bestimmter Code-Block abhängig von einer Bedingung ausgeführt. Die einzelnen Bedingungen und Blöcke werden durch <xsl:when test="">-Blöcke definiert. <xsl:otherwise> definiert den else“-Block. ” Man beachte, dass ein if-then-else-Block mit Hilfe von xsl:choose formuliert werden muss. erzeugt Ausgabetext erzeugt einen Kommentar 15 4 XSL Transformationen <xsl:copy> <xsl:copy-of select="" /> XML-Verarbeitung kopiert das aktuelle Element in den Ausgabestrom kopiert einen Teilbaum, der per XPath im select-Attribut ausgewählt wird, in die Ausgabe Darüber hinaus bietet XSL einige weitere Möglichkeiten, z.B. für die Parametrisierung und Wiederverwendung von Stylesheets und Templates. Es sei auf weiterführende Literatur verwiesen. 4.2 XPath 4.2.1 Grundlagen der Adressierung Eine häufige Aufgabenstellung innerhalb von XSL-Stylesheets ist das Selektieren von Elementen. Dies geschieht navigierend über die Definition von relativen oder absoluten Pfaden. XPath-Ausdrücke erinnern stark an Verzeichnisnamen und sind folgendermaßen aufgebaut: • der Schrägstrich / wird als Separator zwischen Bezeichnern benutzt: event/place • absolute Pfade, die beim Wurzelelement starten, beginnen mit einem Schrägstrich: /tour/artist, alle anderen Pfade sind relativ zum aktuellen Element (dem aktuellen Kontext) definiert. • zwei Punkte verweisen auf das Elternelement des aktuellen Elements: ../place • ein Punkt referenziert das aktuelle Element: ./date Ein Ausdruck wie event/place selektiert alle place-Elemente unter allen eventElementen. Möchte man in dieser Liste ein bestimmtes Element referenzieren, so fügt man den Index in eckigen Klammern an den Bezeichner an. event[1]/place selektiert alle place-Elemente des ersten event-Elements. Falls weitere Operationen Eindeutigkeit erfordern, wird jeweils das erste Element der Liste genutzt. Attributen wird das @-Zeichen vorangestellt: event/place/@name selektiert das nameAttribut des Elements unter event/place. 4.2.2 XPath-Ausdrücke und Wildcards Bedingungen an Elemente werden in eckigen Klammern dem Bezeichner angehängt. place[@name=’ColorLine Arena’] selektiert das place-Element, dessen Attribut name den Wert ColorLine Arena hat. event[date] selektiert alle event-Elemente, die ein date-Unterelement haben. Ein Stern (*) anstelle eines Bezeichners fungiert als Wildcard, er selektiert alle Bezeichner. Analog selektiert @* alle Attribute. Der Ausdruck /*/* selektiert jedes Element auf der zweiten Hierarchieebene. Möchte man ein Element auf einer beliebigen 16 WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 4 XSL Transformationen Ebene selektieren, so schreibt man //: .//place selektiert alle place-Elemente, die irgendwo im aktuellen Teilbaum stehen. 4.3 Ein zusammenhängendes Beispiel Das folgende Listing zeigt ein Stylesheet, das für unser Beispieldokument eine HTMLAusgabe erzeugt: <? xml v e r s i o n=”1 . 0 ” e n c o d i n g=”UTF−8 ” ?> < x s l : s t y l e s h e e t v e r s i o n=”1 . 0 ” x m l n s : x s l=” h t t p : //www. w3 . o r g /1999/XSL/ T r a n s f o r m ”> < x s l : o u t p u t method=”html ”/> < x s l : t e m p l a t e match=”/ ”> < x s l : a p p l y −t e m p l a t e s s e l e c t=”t o u r ” /> </ x s l : t e m p l a t e> < x s l : t e m p l a t e match=”t o u r ”> <html> <head>< t i t l e >V e r a n s t a l t u n g s k a l e n d e r</ t i t l e ></ head> <body> <h1> < x s l : a p p l y −t e m p l a t e s s e l e c t=” a r t i s t ” /> : < x s l : a p p l y −t e m p l a t e s s e l e c t=” t i t l e ” /> </ h1> < t a b l e c o l s=”3 ”> < x s l : a p p l y −t e m p l a t e s s e l e c t=”e v e n t ” /> </ t a b l e> </ body> </ html> </ x s l : t e m p l a t e> < x s l : t e m p l a t e match=” a r t i s t ”> < x s l : v a l u e −o f s e l e c t=” t e x t ( ) ” /> </ x s l : t e m p l a t e> < x s l : t e m p l a t e match=” t i t l e ”> < x s l : v a l u e −o f s e l e c t=” t e x t ( ) ” /> </ x s l : t e m p l a t e> < x s l : t e m p l a t e match=”e v e n t ”> < t r> <t d> < x s l : v a l u e −o f s e l e c t=”d a t e / @day ” /> . < x s l : v a l u e −o f s e l e c t=”d a t e /@month ” /> . < x s l : v a l u e −o f s e l e c t=”d a t e / @ y e a r ” /> </ t d> WS 2004/05, Gunar Fiedler, ISE@CAU 17 4 XSL Transformationen XML-Verarbeitung <t d> < x s l : v a l u e −o f s e l e c t=”d a t e / @hour ” /> : < x s l : v a l u e −o f s e l e c t=”d a t e / @minute ” /> </ t d> <t d> < x s l : v a l u e −o f s e l e c t=”p l a c e /@name ” /> </ t d> </ t r> </ x s l : t e m p l a t e> </ x s l : s t y l e s h e e t> 4.4 XSLT mit Xalan Die Xalan-Bibliothek bietet Klassen, mit deren Hilfe XSL Transformationen durchgeführt werden können. Die zentrale Klasse ist javax.xml.transform.Transformer. Sie erzeugt aus einem Eingabedokument und einem Stylesheet ein Ausgabedokument: // d i e import import import b e n o e t i g t e n Packages j a v a x . xml . t r a n s f o r m . ∗ ; j a v a x . xml . t r a n s f o r m . s t r e a m . ∗ ; j a v a x . xml . t r a n s f o r m . dom . ∗ ; /∗ . . . ∗/ // d i e E i n g a b e d a t e i e n StreamSource s t y l e s h e e t = new S t r e a m S o u r c e ( new F i l e ( ” s t y l e s h e e t . x s l ” ) ) ; StreamSource i n F i l e = new S t r e a m S o u r c e ( new F i l e ( ”i n p u t . xml ” ) ) ; // A u s g a b e d a t e i S t r e a m R e s u l t o u t F i l e = new S t r e a m R e s u l t ( new F i l e ( ”o u t p u t . html ” ) ) ; // E r z e u g e n d e s T r a n s f o r m e r s u e b e r e i n e F a c t o r y TransformerFactory factory = TransformerFactory . newInstance ( ) ; Transformer t r a n s f o r m e r = f a c t o r y . newTransformer ( s t y l e s h e e t ) ; transformer . transform ( inFile , outFile ); Anstelle der StreamSources und StreamResults können auch DOMSources und DOMResults genutzt werden, um DOM-Dokumente zu verarbeiten. Die Xalan-Bibliothek bietet auch einen XSL-Prozessor für die Kommandozeile, der folgendermaßen aufgerufen werden kann: java org.apache.xalan.xslt.Process -IN inFile.xml \ -XSL stylesheet.xsl -OUT outFile.html 18 WS 2004/05, Gunar Fiedler, ISE@CAU XML-Verarbeitung in Java 5 Weiterführende Literatur 5 Weiterführende Literatur • http://www.w3schools.com eine Menge guter Tutorials rund ums Thema XML • http://www.w3.org/TR/REC-xml W3C XML Recommendation • http://www.w3.org/TR/xmlschema-0 W3C XML Schema Recommendation • http://www.w3.org/TR/xsl W3C XSL Recommendation • http://java.sun.com/xml/jaxp/docs.html Dokumentation zu JAXP • http://xml.apache.org Dokumentation zu Xerces und Xalan WS 2004/05, Gunar Fiedler, ISE@CAU 19