XML mit .NET - Programmierung und Basisklassen 3827319986, 9783827319982


293 30 2MB

German Pages 144

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
Inhalt......Page 5
Einführung......Page 7
1.1 XML-Terminologie......Page 9
1.2 Namensräume......Page 11
1.4 Unterstützte Standards......Page 13
2.1 Lesen von XML-Daten......Page 15
2.2 Schreiben von XML-Dateien......Page 21
2.3 Zusammenfassung......Page 26
3 Serialisierung und XML......Page 27
3.1 XML-Serialisierung im Detail......Page 32
3.2 Zusammenfassung......Page 37
4.2 XML-Dokumente laden und lesen......Page 39
4.3 Ändern von XML-Dokumenten per DOM......Page 44
4.4 Ereignisbehandlung......Page 48
4.5 Zusammenfassung......Page 52
5.2 Grundlagen......Page 53
5.3 SelectNodes und SelectSingleNode......Page 59
5.4 Nutzung des XPathNavigators......Page 61
5.5 Zusammenfassung......Page 65
6.1 Einführung......Page 67
6.2 Zusammenfassung......Page 77
7.1 Grundlagen......Page 79
7.2 Stylesheets laden......Page 81
7.3 Transformation durchführen......Page 82
7.4 Transformationen verketten und optimieren......Page 86
7.5 Zusammenfassung......Page 87
8.1 Warum ein Schema?......Page 89
8.2 XSD – der Standard......Page 96
8.3 Zusammenfassung......Page 112
9.2 DataSet und Co.......Page 113
9.3 XmlDataDocument......Page 121
9.4 Zusammenfassung......Page 125
10.1 XML-Kommentare......Page 127
10.2 Zusammenfassung......Page 134
11 Ausblick......Page 135
Stichwortverzeichnis......Page 137
Recommend Papers

XML mit .NET - Programmierung und Basisklassen
 3827319986, 9783827319982

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

XML mit .NET

.NET

Essentials

Armin Hanisch

XML mit .NET Programmierung und Basisklassen

An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam

Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich. Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Abbildungen und Texten wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Produkt wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie – zum Schutz vor Verschmutzung – ist aus umweltverträglichem und recyclingfähigem PE-Material.

5 05

4 04

3

2

1

03

02

ISBN 3-8273-1998-6 © 2002 by Addison-Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Barbara Thoben, Köln Lektorat: Christine Auf, [email protected], Tobias Draxler, [email protected] Korrektorat: Katja Treu, München Herstellung: Monika Weiher, [email protected] Satz: reemers publishing services gmbh, Krefeld, www.reemers.de Druck und Verarbeitung: Media-Print, Paderborn Printed in Germany

.NET Essentials

Inhalt Einführung

7

Vorwort Voraussetzungen Typografie Beispielcodes, Errata, Hinweise

7 7 8 8

1

XML-Basics

9

1.1 1.2 1.3 1.4

XML-Terminologie Namensräume Die .NET-Namensräume Unterstützte Standards

9 11 13 13

2

XmlReader und XmlWriter

15

2.1 2.2 2.3

Lesen von XML-Daten Schreiben von XML-Dateien Zusammenfassung

15 21 26

3

Serialisierung und XML

27

3.1 3.2

XML-Serialisierung im Detail Zusammenfassung

32 37

4

Document Object Model

39

4.1 4.2 4.3 4.4 4.5

Streaming oder DOM? XML-Dokumente laden und lesen Ändern von XML-Dokumenten per DOM Ereignisbehandlung Zusammenfassung

39 39 44 48 52

5

XPath-Abfragen

53

5.1 5.2 5.3 5.4 5.5

Warum XPath? Grundlagen SelectNodes und SelectSingleNode Nutzung des XPathNavigators Zusammenfassung

53 53 59 61 65

6

XML-Namensräume

67

6.1 6.2

Einführung Zusammenfassung

67 77

5

.NET

Inhalt ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

6

7

XSL-Transformationen

79

7.1 7.2 7.3 7.4 7.5

Grundlagen Stylesheets laden Transformation durchführen Transformationen verketten und optimieren Zusammenfassung

79 81 82 86 87

8

Validieren von XML

89

8.1 8.2 8.3

Warum ein Schema? XSD – der Standard Zusammenfassung

89 96 112

9

XML und Daten

113

9.1 9.2 9.3 9.4

Einführung DataSet und Co. XmlDataDocument Zusammenfassung

113 113 121 125

10

XML-Dokumentation

127

10.1 10.2

XML-Kommentare Zusammenfassung

127 134

11

Ausblick

135

Stichwortverzeichnis

137

.NET Essentials

Einführung Vorwort Thema dieses Bandes sind die XML-Basisklassen des .NET-Frameworks und ihre Verwendung sowie die Rolle von XML als Basisinfrastruktur innerhalb der .NET-Umgebung. XML ist bereits seit einigen Jahren dabei, sich zur neuen ‚lingua franca’ innerhalb der IT-Welt zu entwickeln und auch Microsoft setzt im Rahmen der .NET Initiative auf XML. Es gibt wohl kaum einen Bereich innerhalb des .NET-Frameworks, in dem XML keine Rolle spielt. Als universelles Austausch- und Speicherformat wird es für Konfigurationsdateien, ADO.NET, die Objektserialisierung, Schemas und XSLT bis hin zu SOAP und Web Services als sprachneutrales Format benutzt. Als Entwickler haben Sie kompletten Zugriff auf eine Reihe von hochoptimierten und vollständigen Klassen, die nahezu jede Anforderung abdecken und über den Funktionsumfang der alten XML Core Services der COM-Welt (MSXML) weit hinausgehen. Mit nur wenigen Zeilen an Code sind Sie in der Lage, Daten zu transformieren, XML-Strukturen aufzubauen oder zu validieren und mit ADO.NET sogar beliebige XML-Dateien entweder als relationales DataSet oder als XML-Baumstruktur zu behandeln.

Voraussetzungen Benötigte Software Zum Nachvollziehen der Beispiele in diesem Buch benötigen Sie einen Rechner mit Windows 2000 oder Windows XP, Visual Studio .NET oder das .NET-Framework SDK. Die Beispiele zu diesem Buch finden Sie zum Download auf der Website für diese Buchreihe, die unter dem URL http://www.dotnet-essentials.de erreichbar ist. Dieses Buch geht davon aus, dass Sie zumindest das .NET-Framework und die Kommandozeilen-Tools installiert haben. Sie benötigen nicht unbedingt Visual Studio .NET, obwohl Sie im Kapitel über XML-Validierung und XMLDokumentation sicher davon profitieren, da die grafische Oberfläche doch einige Aufgaben erleichtert. Aber auch diese Kapitel enthalten Hinweise, wie Sie ohne das Visual Studio .NET zurechtkommen.

7

.NET

Einführung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Vorkenntnisse Die Zielgruppe dieser Buchreihe sind Entwickler mit Programmiererfahrung, die sich bereits erfolgreich mit den Grundlagen von .NET und Visual Studio vertraut gemacht haben. Sie finden in diesen Bänden praktische Beispiele und kompakte Informationen ohne die endlose Wiederholung von Grundlagen. Der Autor geht davon aus, dass Sie C# beherrschen bzw. Code, der in C# geschrieben wurde, lesen können und mit der objektorientierten Programmierung vertraut sind. Aus Platzgründen sind viele Listings nicht komplett abgedruckt, sondern zeigen lediglich den Code einer (meist statischen) Methode ohne die Klassendefinition und die using-Anweisungen drumherum, da Sie sicher in der Lage sind, diese zu ergänzen. Wo es sinnvoll erschien oder bestimmte zusätzliche Namespaces eingebunden wurden, wurden komplette Listings abgedruckt. Die Quellcodes der Downloads sind natürlich alle vollständig.

Typografie Normaler Fließtext wird mit dieser Schriftart dargestellt. Falls innerhalb eines Textabsatzes einzelne Schlüsselwörter oder Klassen- bzw. Variablennamen vorkommen, werden diese als Klassenname hervorgehoben. Listings im Text besitzen diese Schriftart und wurden nach Möglichkeit so umbrochen, dass die Struktur trotz der geringen Spaltenbreite erhalten bleibt. Einzelne Listingszeilen erhalten diese Formatierung

Auswahlen in einem MENÜNAMEN oder als DIALOGOPTION werden ebenfalls besonders gekennzeichnet. Alle Dateinamen, URLs oder Pfadangaben sind wie hier gezeigt hervorgehoben.

Beispielcodes, Errata, Hinweise Die Beispielcodes zu diesem Band der Reihe .NET-Essentials finden Sie im Internet unter dem URL http://www.DotNet-Essentials.de. Hier finden Sie auch zusätzliche Hinweise, Ergänzungen und Korrekturen zu diesem und den anderen Bänden der Reihe. Alle Listings, Codeangaben und Verweise in diesem Buch wurden nach bestem Wissen und Gewissen auf Fehlerfreiheit geprüft. Sollten Sie dennoch einen Fehler finden oder Anregungen und Kommentare zu diesem Buch haben, freut sich der Autor auf Ihr Feedback.

8

.NET

Essentials

1

XML-Basics

1.1 XML-Terminologie Knoten, Elemente, Tags und Attribute Eine XML-Struktur (Struktur deshalb, da XML nicht notwendigerweise immer in einer Datei vorliegen muss, beispielsweise als MemoryStream) ist eine baumförmige Struktur. Dieser Baum besteht aus einzelnen Knoten oder Elementen, die in textueller Form durch entsprechende Tags dargestellt werden. Ein Element kann einen reinen Textinhalt oder Unterelemente (oder Kindknoten) besitzen, auch ein gemischter Inhalt aus Text und Unterelementen ist möglich. Je nach Programmiermodell (Streamingzugriff über XmlReader oder DOM-Zugriff über ein XmlDocument) wird diese XML-Struktur seriell gelesen und Element für Element an die Anwendung weitergegeben oder die komplette Datei wird gelesen und die Baumstruktur im Speicher aufgebaut (wobei hier der Ressourcenbedarf dementsprechend groß werden kann). Ein Element kann entweder ein XML-Element, ein Kommentar, eine Verarbeitungsanweisung an den XML-Parser oder Leerraum sein.

XML als Baum oder als Stream

Elemente können Attribute enthalten, die allerdings in keiner bestimmten Reihenfolge vorliegen müssen (der XML-Standard erlaubt ausdrücklich eine wechselnde Attributreihenfolge bei gleichen Elementen) und die daher in den XML-Klassen in einer ungeordneten Auflistung mitgeführt werden. Attribute besitzen einen Namen und einen Wert.

XML-Zeichen und XML-Einheiten Eine XML-Datei besteht aus verschiedenen Einheiten: Kommentaren, Leerraum, Verarbeitungsanweisungen oder Elementen. Auch hier gelten einige Regeln, ebenso wie für die Angabe von bestimmten Zeichen innerhalb einer XML-Datei. Leerraum wird innerhalb einer XML-Datei ignoriert oder beim Parsing mit beachtet, abhängig von der Einstellung der entsprechenden Klasse. Im .NET-Framework geschieht dies über eine Eigenschaft mit dem Namen PreserveWhitespace. Ist Leeraum innerhalb eines Elements von Bedeutung, beispielsweise für die Einrückung in einem Listing, dann sollten Sie das Attribut xml:space verwenden und einen Wert von "preserve" setzen. Der Defaultwert "default" legt fest, dass die Einstellungen des Parsers verwendet werden.

Leerraum

9

.NET

1 XML-Basics ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Kommentare

Eín Kommentar beginnt auch in XML mit der Zeichenfolge

Document

Das Dokumentenobjekt, welches die Wurzel des XMLBaums darstellt.

DocumentFragment

Ein XML-Fragement beinhaltet einen Knoten oder einen Teilbaum, nicht notwendigerweise in einem eigenen Dokument.

DocumentType

Der Dokument-Typ, z.B.

Element

Ein einzelnes XML-Element, z.B.

EndElement

Ein schließendes Tag für ein Element, z.B.

Tabelle 2.1: Die von XmlReader gelieferten Knotentypen

16

Lesen von XML-Daten ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Name

Beschreibung

EndEntity

Wird als Ergebnis einer Entity-Auflösung mit ResolveEntity zurückgeliefert und markiert das Ende eines Entity.

Entity

Die Deklaration eines XML-Entity, z.B.

EntityReference

Die Referenz auf ein Entity, z.B. ä

None

Wird geliefert, wenn noch nie die Methode Read aufgerufen wurde.

Notation

Ein Notationseintrag in der DTD, z.B.

ProcessingInstruction

Eine Verarbeitungsanweisung, z.B.

SignificantWhitespace

Leerraum, der beachtet werden soll, wenn z.B. die Option xml:space="preserve" verwendet wurde.

Text

Der Textinhalt eines XML-Knotens

Whitespace

Leerraum zwischen Auszeichnungsknoten

XmlDeclaration

Die XML-Deklaration selbst, z.B. ; diese Anweisung muss der erste Knoten im Dokument sein.

Tabelle 2.1: Die von XmlReader gelieferten Knotentypen (Fortsetzung)

Knotentypen unterscheiden Für die nachfolgende kleine XML-Datei, die in den nächsten Beispielen benutzt wird, soll nun jeweils der Knotentyp sowie der Name des Elements ausgegeben werden.

Algeirs overcast 11 93% E 5 1028 rising

Athens mostly cloudy 16 77% SW 2 1024

17

.NET

2 XmlReader und XmlWriter ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

rising

Listing 2.2: Die Beispieldatei mit XML-Inhalt

Da sich der Leerraum (white space) schlecht anzeigen lässt, erfolgt die Ausgabe hier über die Klasse Convert als Hexdump der Zeichencodes. // neue methode ReadXmlDoc mit Knotenbehandlung using System; using System.XML; using System.Text; namespace XmlReader { class clsReaderClass { XmlTextReader rdr; public clsReaderClass(string docurl) { rdr = new XmlTextReader(docurl); } public void ReadXmlDoc() { while (rdr.Read()) { // den typ des knotens als string ausgeben Console.Write(rdr.NodeType); // abhängig vom typ ausgabe bauen switch(rdr.NodeType) { // ein element: name ausgeben case XmlNodeType.Element: Console.Write(" "); break; // schliessendes tag: / + name case XmlNodeType.EndElement: Console.Write(" "); break; // textinhalt: achtung! value statt name! case XmlNodeType.Text: Console.Write(" " + rdr.Value); break;

18

Lesen von XML-Daten ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

// whitespace: zeichen als hexdump ausgeben case XmlNodeType.SignificantWhitespace: case XmlNodeType.Whitespace: for(int i = 0; i < rdr.Value.Length; i++) { int cv = Convert.ToUInt16(rdr.Value[i]); Console.Write(" {0:X2}", cv); } break; } Console.WriteLine(); } rdr.Close(); } // // Die Testklasse für clsReaderClass // class TestClass { static void Main(string[] args) { clsReaderClass rc; rc = new clsReaderClass("miniwetter.xml"); rc.ReadXmlDoc(); } } } Listing 2.3: Unterscheidung der einzelnen Knotentypen

Hier die Ausgabe des Programms bis zum Ende des ersten stationKnotens: XmlDeclaration Whitespace 0D 0A Element Whitespace 0D 0A 09 Element Whitespace 0D 0A 09 09 Element Text Algeirs EndElement Whitespace 0D 0A 09 09 Element Text overcast EndElement Whitespace 0D 0A 09 09 Element

19

.NET

2 XmlReader und XmlWriter ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Text 11 EndElement Whitespace 0D 0A 09 09 Element Text 93% EndElement Whitespace 0D 0A 09 09 Element Text E EndElement Whitespace 0D 0A 09 09 Element Text 5 EndElement Whitespace 0D 0A 09 09 Element Text 1028 EndElement Whitespace 0D 0A 09 09 Element Text rising EndElement Whitespace 0D 0A 09 EndElement Listing 2.4: Die Ausgabe der einzelnen Knoten

Fehlerbehandlung beim Lesen Wird die Wohlgeformtheit des Dokuments durch eine ungültige XMLKonstruktion verletzt, wird eine XmlException ausgelöst und der Lesevorgang abgebrochen.

Lesen bestimmer Elemente und Datentypen Leere Elemente finden

Für viele Anwendungszwecke muss sichergestellt werden, dass der aktuelle Knoten ein bestimmtes Element enthält. Hierzu exisitieren weitere Methoden der XmlReader-Klasse, die ein bestimmtes Element erwarten. So kann mit der Methode IsStartElement festgestellt werden, ob der aktuelle Knoten ein Starttag ist, während beispielsweise IsEmptyElement dann true liefert, wenn es sich um ein »leeres« Element in der Form handelt. Die Methode ReadStartElement erlaubt das Lesen ganz bestimmter Elemente und ein manuelles Parsing der Datei, so dass eine Exception ausgelöst wird, wenn das Element nicht den erwarteten Namen besitzt.

20

Schreiben von XML-Dateien ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

2.2 Schreiben von XML-Dateien Das Schreiben von XML-Daten als Strom erfolgt mit Hilfe einer von der abstrakten Klasse XmlWriter abgeleiteten Klasse. In der aktuellen Version des .NET-Frameworks existiert eine Implementierung in der Klasse XmlTextWriter, die auch für die folgenden Beispiele benutzt wird.

Instantiierung der Klasse Der Konstruktor der Klasse XmlTextWriter ist dreifach überladen. Sie akzeptiert entweder einen vorhandenen TextWriter, einen Stream oder einen Dateinamen als Parameter, wobei für den Dateinamen und den IO-Stream als zweiter Parameter noch die Codierung der Zeichen angegeben werden kann (wird dieser auf null gesetzt, erfolgt die Ausgabe mit der Standardcodierung UTF-8). // instanz per dateinamen erzeugen // die datei wird bei existenz neu überschrieben XmlTextWriter xtw = new XmlTextWriter(@"c:\tmp\ausgabe.xml", null); Listing 2.5: Beispiel-Instantiierung per Dateinamen für XmlTextWriter

Schreiben von Elementen und Attributen Die Methoden zum Erstellen des Inhaltes sind leicht zu finden und anzuwenden, deren Namen sind ebenfalls weitestgehend selbsterklärend. Das folgende Beispiel zeigt eine Methode zum Schreiben einer einfachen XML-Datei. public static void DemoXmlTextWriter() { // instanz erzeugen XmlTextWriter xtw = new XmlTextWriter(@"c:\tmp\ausgabe.xml", null); // inhalte schreiben xtw.WriteStartElement("buecher"); xtw.WriteElementString("titel", "Von Windows verweht ..."); xtw.WriteStartElement("preis"); xtw.WriteAttributeString("waehrung","EUR"); xtw.WriteString("14.55"); xtw.WriteEndElement(); xtw.WriteEndElement(); // writer schliessen

21

.NET

2 XmlReader und XmlWriter ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

xtw.Close(); Console.WriteLine("Datei geschrieben."); } Listing 2.6: Schreiben von XML, die erste Version

WriteStartElement vs. WriteElement String

Wie Sie aus dem Listing erkennen, existiert zum Schreiben von Elementen eine Methode WriteElementString, die Starttag, Inhalt und Endtag in einem Aufruf schreibt. Soll das Element jedoch Unterelemente oder Attribute enthalten, dann benutzen Sie besser die Methoden WriteStartElement und WriteEndElement, mit denen Sie die einzelnen Elemente gezielt beginnen und beenden können. Die Ausgabe des Programms in der Datei c:\tmp\ausgabe.xml sieht dann so aus: Von Windows verweht ...14.55 Listing 2.7: Inhalt der erzeugten Datei

Hier sind noch einige Sachen zu verbessern. Zuerst ist dies keine gültige XML-Datei, noch nicht mal eine wohlgeformte, da die Verarbeitungsanweisung fehlt. Außerdem wäre eine für den (menschlichen) Entwickler etwas bessere Lesbarkeit recht schön. Diese beiden Punkte erreichen Sie durch ein paar Änderungen im Code. Zuerst werden die beiden Methoden WriteStartDocument und WriteEndDocument integriert, die für einen sauberen »Rahmen« um die XML-Struktur sorgen. Danach wird die Formatierung noch umgestellt. Hier die Änderungen im Code: public static void DemoXmlTextWriter() { // instanz erzeugen XmlTextWriter xtw = new XmlTextWriter(@"c:\tmp\ausgabe.xml", null); // formatierung einstellen // pro ebene 1 tab eingerückt xtw.Indentation = 1; xtw.IndentChar = '\t'; xtw.Formatting = Formatting.Indented; // inhalte schreiben xtw.WriteStartDocument(); xtw.WriteStartElement("buecher"); xtw.WriteElementString("titel", "Von Windows verweht ..."); xtw.WriteStartElement("preis"); xtw.WriteAttributeString("waehrung","EUR");

22

Schreiben von XML-Dateien ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

xtw.WriteString("14.55"); xtw.WriteEndElement(); xtw.WriteEndElement(); xtw.WriteEndDocument(); // writer schliessen xtw.Close(); Console.WriteLine("Datei geschrieben."); } Listing 2.8: Ausgabe mit XML-PI und Formatierung

Jetzt findet sich in der Ausgabe auch eine wohlgeformte XML-Struktur mit einer lesbaren Formatierung:

Von Windows verweht ... 14.55

Listing 2.9: Inhalte der Ausgabedatei

Nachdem es im Gegensatz zu beispielsweise HTML bei XML immer ein schließendes Tag geben muss, werden Elemente ohne Inhalt in der Notation geschrieben. Viele Programme kommen damit aber nicht zurecht (vor allem ältere Browser), daher kann anstelle der Methode WriteEndElement die Methode WriteFullEndElement benutzt werden, die eine Ausgabe in der Form erzeugt. Neben der Methode WriteString zum Schreiben von Textinhalten existieren noch Methoden zum Schreiben von Binärdaten in BinHex- oder Base64-Notation, als CDATA-Bereich oder als Entity-Referenz für Unicode-Zeichen. Alle diese Methoden folgenden aber dem gleichen Prinzip, so dass ein eigenständiges Beispiel für jede dieser Methoden hier nicht notwendig ist.

XML mit XmlWriter und XmlReader modifzieren Als Abschluss für dieses Kapitel ein kleines Beispiel, wie mit Hilfe dieser Streamingklassen auch große Dateien ressourcenschonend verarbeitet werden können. Nehmen wir an, in einer als XML-Datei vorliegenden Bücherliste soll die Umstellung von DEM auf EUR vorgenommen werden (gut, dass wir das schon alle hinter uns haben!). Mit einem XmlTextReader werden die einzelnen Knoten gelesen und über eine XmlTextWriterInstanz in die Ausgabedatei geschrieben. Wird ein Element mit dem Namen »Preis« angetroffen, wird dessen Textinhalt von Mark nach Euro

23

.NET

2 XmlReader und XmlWriter ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

umgerechnet und ein evtl. vorhandenes Attribut »Währung« ebenfalls korrekt umgestellt. // umwandeln von xml-dateien per streaming-zugriff public static void DemoModifyXml(string inURL, string outURL) { // die reader und writer XmlTextReader rdr = new XmlTextReader(inURL); XmlTextWriter wrt = new XmlTextWriter(outURL, null); // ausgabe einruecken wrt.Formatting = Formatting.Indented; wrt.Indentation = 4; wrt.IndentChar = (char)32; // marker fuer preis-element bool bPreistag = false; // dokumenten-start-pi schreiben wrt.WriteStartDocument(); // alle knoten lesen while (rdr.Read()) { // abhängig vom typ ausgabe bauen switch(rdr.NodeType) { // element, auf "preis" testen case XmlNodeType.Element: bPreistag = (rdr.Name == "preis"); if(bPreistag) Console.WriteLine("{0} gefunden!", rdr.Name); wrt.WriteStartElement(rdr.Name); // attribute mit kopieren if(rdr.HasAttributes) { for(int i = 0;i < rdr.AttributeCount;i++) { rdr.MoveToAttribute(i); // waehrung ist jetzt EUR // also eintragen if(rdr.Name == "waehrung") wrt.WriteAttributeString(rdr.Name,"EUR"); else wrt.WriteAttributeString(rdr.Name,rdr.Value); } } break;

24

Schreiben von XML-Dateien ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

// schliessendes tag: / + name case XmlNodeType.EndElement: if(rdr.Name == "preis" && bPreistag) { bPreistag = false; Console.WriteLine("/{0} gefunden!", rdr.Name); } wrt.WriteEndElement(); break; // textinhalt: achtung! value statt name! case XmlNodeType.Text: if(bPreistag) { decimal betrag = decimal.Parse(rdr.Value); betrag = betrag / 1.955830m; Console.WriteLine("Konvertiert!"); wrt.WriteString(betrag.ToString("F2")); } else wrt.WriteString(rdr.Value); break; } } rdr.Close(); wrt.WriteEndDocument(); wrt.Close(); } Listing 2.10: XML-Konvertierung manuell

Hier die beiden Dateien, zuerst die Eingabe-, dann die Ausgabedatei nach der Konvertierung:

Von Windows verweht ... 29,90

Alice im Windowsland 34,80

Listing 2.11: Die Eingabedatei

25

.NET

2 XmlReader und XmlWriter ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials



Von Windows verweht ... 15,29

Alice im Windowsland 17,79

Listing 2.12: Die Ausgabedatei

2.3 Zusammenfassung Die Klassen XmlReader und XmlWriter sowie die davon abgeleiteten Klassen bieten einen ressourcenschonenden Zugriff auf XML-Dateien in Streaming-Form. Sie haben erfahren, wie sich feststellen lässt, welchen Knoten Sie gerade lesen, wie Elemente und Attribute geschrieben werden und wie mit Hilfe dieser Klassen auf einfache Art und Weise kleinere Änderungen in XML-Dateien durchgeführt werden können. Dieser rein lesend bzw. schreibende, nur vorwärtsgerichtete Zugriff reicht aber nicht aus, um alle Szenarien abzudecken. Für komplexere Änderungen oder die Navigation innerhalb der XML-Struktur reicht dieser Ansatz nicht mehr aus. Daher erfahren Sie im nächsten Kapitel, wie Sie den Zugriff über das Document Object Model realisieren. Mit dieser Schnittstelle können Sie jeden Knoten eines Dokuments einzeln nach Ihren Anforderungen anspringen und ändern.

26

.NET

Essentials

3

Serialisierung und XML

Einführung Serialisierung bedeutet, vereinfacht ausgedrückt, das »Überleben« der Objekte nach einem Programmende oder die Verpackung der Objekte für den Transport über Prozess- und Maschinengrenzen bei einem Remote-Aufruf. Für OOP-Systeme, die Mechanismen für solche Aktionen boten, wurde dies dadurch gewährleistet, dass aktuelle Daten und Variableninhalte in eine Datei geschrieben wurden. Der Code lag in der ausführbaren Datei vor und nach dem erneuten Start wurde die Datendatei geöffnet und der Entwickler hatte die Aufgabe, alle Daten wieder in die richtigen Strukturen zu laden. Innerhalb des .NET-Frameworks ist XML das Rückgrat für die Serialisierung von Klasseninstanzen. Die für die Nutzung dieser Funktionalität benötigten Namensräume des .NET-Frameworks sind System.XML und System.XML.Serialization.

Terminologie

Serialisierung mit XmlSerializer Die Klasse XmlSerializer ist für die Serialisierung einer Instanz in eine XML-Datei bzw. den umgekehrten Weg zurück zu einer Instanz zuständig. In der einfachsten Form erhält der Konstruktor einen Parameter mit dem Typ des Objekts, für das er zuständig sein soll. Hier eine sehr einfache Demoklasse, die in eine XML-Datei serialisiert werden soll: // eine einfache demo-klasse public class SimpleDemo { public string Kennzeichen; public int KmStand; public double Verbrauch; } Listing 3.1: Die erste Demoklasse

Als Ziel für die Serialisierung können Sie bei der Methode Serialize des XmlSerializer drei mögliche Ausgabetypen spezifizieren:

왘 Einen XmlWriter oder einen Nachfahren 왘 Einen Stream oder einen Nachfahren 왘 Einen TextWriter oder einen Nachfahren

27

.NET

3 Serialisierung und XML ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Nachdem es hier nur um das Herauschreiben der Instanzdaten geht, verwenden wir im Listing einen XmlWriter, da dieser als reine vorwärtsgerichtete Nur-Schreiben-Klasse den geringsten Overhead besitzt. Hier der Code, mit dem die Instanz serialisiert wird: class TestClass { static void Main(string[] args) { XmlSerializer ser = new XmlSerializer(typeof(SimpleDemo)); XmlTextWriter tw = new XmlTextWriter("ausgabe.xml", null); SimpleDemo d = new SimpleDemo(); d.Kennzeichen = "LA-DY 2002"; d.KmStand = 45800; d.Verbrauch = 7.85; ser.Serialize(tw, d); tw.Close(); Console.WriteLine("Objekt serialisiert."); Console.Write("\nPress a key ...."); Console.ReadLine(); } } Listing 3.2: Serialisierung in eine XML-Datei

Nach dem erfolgreichen Durchlaufen des Codes liegt die Datei auf der Platte und Sie können sich deren Inhalt ansehen: Objekte als XML

LA-DY 2002 45800 7.85

Listing 3.3: Der Inhalt der XML-Ausgabedatei

Sie erkennen, dass das Wurzelelement den Namen der Klasse trägt und die einzelnen Elemente mit ihrer Bezeichnung den Namen der Felder entsprechen. Zusätzlich werden die beiden Deklarationen der Namens-

28

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

räume für XML-Schemas und XML-Schemainstanzen mit in das Wurzelelement aufgenommen (mehr zu Namensräumen erfahren Sie im Kapitel »XML-Namensräume«).

Deserialisierung mit XmlSerializer Um aus einer XML-Datei eine Instanz einer Klasse zu laden, gehen sie einfach den umgekehrten Weg. class TestClass { static void Main(string[] args) { XmlSerializer ser = new XmlSerializer(typeof(SimpleDemo)); // diesmal einen reader einsetzen XmlTextReader rdr = new XmlTextReader("ausgabe.xml"); SimpleDemo d; // klasseninstanz aus dem serializer d = ser.Deserialize(rdr) as SimpleDemo; rdr.Close(); // instanzdaten ausgeben Console.WriteLine("Objekt deserialisiert:"); Console.WriteLine(d.Kennzeichen); Console.WriteLine(d.KmStand); Console.WriteLine(d.Verbrauch); Console.Write("\nPress a key ...."); Console.ReadLine(); } } Listing 3.4: Deserialisierung aus einer XML-Datei

Abbildung 3.1: Die Ausgabe des Deserialisierungsbeispiels

29

.NET

3 Serialisierung und XML ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Dieser Code produziert die in Abbildung 3.1 zu sehende Ausgabe und zeigt damit, dass so sehr leicht aus einer XML-Datei eine Objektinstanz erzeugt werden kann.

Ungleiche Dokumente und Klassen Falls ein Schema vorliegt, können Sie mit dem folgenden Aufruf aus dem Schema eine passende Klasse erstellen lassen, um dieses Problem zu vermeiden. Das Tool xsd.exe

xsd schema0.xsd /classes Anschliessend wird eine Datei schema0.cs erzeugt, die eine Klasse mit den aus dem Schema gewonnen Feldern enthält. //----------------------------------------------------// // This code was generated by a tool. // Runtime Version: 1.0.3705.0 // // Changes to this file may cause // incorrect behavior and will be lost if // the code is regenerated. // //----------------------------------------------------// // This source code was auto-generated by xsd, Version=1.0.3705.0. // using System.XML.Serialization;

/// [System.XML.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)] public class SimpleDemo { /// public string Kennzeichen; /// public int KmStand; /// public System.Double Verbrauch; } Listing 3.5: Automatisch aus einem Schema erzeugte Klasse

30

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Was aber passiert nun, wenn die Definition der Klasse nicht mit dem Aufbau der Datei übereinstimmt? Eine Möglichkeit wäre es, nur Dateien anzunehmen, deren Aufbau genau der Klasse entspricht, indem für die Klasse ein Schema erstellt und das Dokument damit validiert wird. Ein Beispiel dafür finden Sie im Kapitel über die Validierung von XML-Dokumenten. Für den anderen Fall wird die XML-Datei um ein zusätzliches Element ergänzt, das in der Klasse nicht vorhanden ist. Ebenso wird die Klasse um ein weiteres Feld ergänzt, das in der XML-Datei nicht vorhanden ist.

Zusätzlicher Inhalt in der XML-Datei

LA-DY 2002 45800 108 7.85

Listing 3.6: Die geänderte XML-Datei // eine einfache demo-klasse public class SimpleDemo { public string Kennzeichen; public int KmStand; public double Verbrauch; public string Hersteller; } Listing 3.7: Die geänderte Klasse mit dem neuen Feld

Bei der Ausführung des Programms erfolgt keine Fehlermeldung und die Ausgabe zeigt das Standardverhalten bei der Behandlung unbekannter Elemente in der XML-Datei:

Abbildung 3.2: Deserialisierung mit den geänderten Daten

31

.NET

3 Serialisierung und XML ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Elemente ignorieren

Zusätzlich vorhandene Elemente werden also bei der Deserialisierung ignoriert, Felder in der Klasse ohne einen Inhalt in der XML-Datei werden von der Laufzeitumgebung bei der Erzeugung der Instanz initialisiert, bekommen dann aber keinen Wert aus der XML-Datei zugewiesen. Dieses Verhalten lässt sich, wie in den spätereren Abschnitten gezeigt, auch steuern und beinflussen.

3.1 XML-Serialisierung im Detail Steuerung der XML-Struktur Den Serialisierungsprozess, genauer die Erstellung der XML-Datei, können Sie über den Einsatz verschiedener Attribute steuern. So möchten Sie vielleicht einige Felder der Klasse nicht als eigene Unterelemente, sondern als Attribute des Klassenelementes rendern lassen oder für einige Elemente den Datentyp ändern. Serialisierungs-Attribute

Um das Wurzelelement der XML-Datei zu ändern, benutzen Sie vor der Klasse das Attribut [XmlRoot()]. Das Attribut [XmlElement] dient der Definition eines anderen Elementnamens, der vom Namen des Feldes abweicht. Mit Hilfe des Attributes [XmlAttribute] können Sie angeben, dass ein Feld nicht als eigenes Element, sondern als Attribut des umgebenden Elternelements gerendert wird. // eine einfache demo-klasse [XmlRoot(ElementName = "root")] public class SimpleDemo { public string Kennzeichen; public int KmStand; [XmlIgnore] public double Verbrauch; [XmlAttribute(AttributeName = "marke")] public string Hersteller; } Listing 3.8: Die Klasse mit XML-Steuerungsattributen

Wird die geänderte Klasse jetzt serialisiert, enthält die XML-Datei die neuen Daten, wobei das mit dem Attribut [XmlIgnore] deklarierte Feld aus der Datei ausgelassen wird.

32

XML-Serialisierung im Detail ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

LA-DY 2002 45800

Listing 3.9: Der neue Inhalt der XML-Datei

Serialisierung komplexerer Datentypen Für die Serialisierung von Datentypen wie Eigenschaften, Arrays, geschachtelten Objekten usw. gilt im Prinzip das bereits weiter oben gesagte. Für die folgenden Beispiele wird eine etwas erweiterte Klassendefinition benutzt, die über eine öffentliche Eigenschaft, ein Array und Konstruktoren verfügt. Hier der Teil der Klassendefinition mit den entsprechenden Feldern: // demo-klasse für die serialisierung public class DemoClass { // private felder der klasse private int fID; private int fIndentLevel = 0; // öffentliche felder public string StrField = "Nur ein String"; public System.UInt32[] liste = {1,3,5,7,11,13,17,19}; // konstruktoren public DemoClass() { } public DemoClass(int theID) { fID = theID; } // eine öffentliche eigenschaft public int IndentLevel { get { return fIndentLevel; } set { fIndentLevel = value; } } Listing 3.10: Der erste Teil der erweiterten Klassendefinition

Diese Klasse soll in der Lage sein, sich selbst zu serialisieren und über eine statische Methode aus einer anzugebenden XML-Datei die Deserialisierung durchzuführen. Dies wird in die beiden Methoden SaveToXml und LoadFromXml eingepackt.

33

.NET

3 Serialisierung und XML ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

// serialisieren in eine xml-datei public void SaveToXml(string filename) { XmlSerializer ser = new XmlSerializer(typeof(DemoClass)); // einen xmlwriter einsetzen XmlTextWriter xml = new XmlTextWriter(filename, null); xml.Formatting = Formatting.Indented; xml.Indentation = 4; xml.IndentChar = (char)32; // klasseninsanz serialisieren ser.Serialize(xml, this); xml.Close(); } // deserialisieren aus einer xml-datei public static object LoadFromXml(string filename) { XmlSerializer ser = new XmlSerializer(typeof(DemoClass)); XmlTextReader rdr = new XmlTextReader(filename); if(ser.CanDeserialize(rdr)) return ser.Deserialize(rdr); else return null; } } // ende der klassendefinition Listing 3.11: Die beiden Wrapper für die Serialisierung

Arrays serialisieren

Wird eine Instanz dieser Klasse über den Aufruf von SaveToXml in eine XML-Datei geschrieben, wird eine öffentliche Eigenschaft als XML-Element mit dem Namen dieser Eigenschaft geschrieben (also analog zu einem normalen öffentlichen Feld). Das Array wird als XML-Element mit dem Namen des Arrays erstellt und die einzelnen Elemente dieses Arrays werden als Unterelemente, jeweils mit dem Namen des Datentyps, in die Datei geschrieben. Hier die erstellte XML-Datei:

Hallo!!

34

XML-Serialisierung im Detail ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

1 3 5 7 11 13 17 19

4

Listing 3.12: Die serialisierte Instanz als XML-Datei

Für die Deserialisierung muss ein parameterloser Konstruktor in der Klasse vorhanden sein (Sie haben sich vielleicht schon darüber gewundert). Da der XmlSerializer aus den Daten der XML-Datei nicht erschließen kann, welcher Konstruktor mit welchen Parametern aufzurufen ist, muss ein Konstruktor ohne Parameter vorhanden sein, der dann aufgerufen wird, um die Instanz zu erstellen. Über das Einlesen der restlichen XML-Elemente werden anschließend die gleichlautenden öffentlichen Felder und Eigenschaften gesetzt.

Unbekannten XML-Inhalt behandeln Sollte in der XML-Datei zusätzlicher Inhalt vorhanden sein (in Form von Elementen, Attributen oder z.B. CDATA-Knoten), dann können Sie über Ereignisbehandlungsroutinen das Standardverhalten der Deserialisierung ändern. Sind keine solchen Eventhandler definiert, wird zusätzlicher Inhalt in der XML-Datei einfach ignoriert und für Felder der Klasse, für die kein Element in der Datei vorhanden ist, wird keine Zuweisung durchgeführt. Für die Steuerung dieses Verhaltens definiert die Klasse XmlSerializer drei Ereignisse: UnknownElement, UnknownAttribute und UnknownNode. Erstellen Sie eine Ereignisbehandlungsroutine für das gewünschte Ereignis, wird diese Methode dann aufgerufen. Für eine Demonstration erweitern wir die Datei aus Listing 3.3 um zusätzliche Elemente.

Unknown-Ereignisse

LA-DY 2002 Renault 45800 7.85

35

.NET

3 Serialisierung und XML ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Listing 3.13: Der neue Inhalt der XML-Datei

Jetzt wird dafür eine Ereignisbehandlungsroutine definiert: public void XmlElementEventHandler(object sender, XmlElementEventArgs args) { Console.Write("Bei der Deserialisierung von {0} ", args.ObjectBeingDeserialized.ToString()); Console.Write("wurde ein Element gefunden.", args.Element.LocalName); Console.WriteLine(); } Listing 3.14: Der EventHandler für unbekannte Elemente

Diese Methode wird dem XmlSerializer nach der Instantiierung eingetragen. static void Main(string[] args) { XmlSerializer ser = new XmlSerializer(typeof(SimpleDemo)); XmlTextReader rdr = new XmlTextReader(@"c:\tmp\ser.xml"); ser.UnknownElement += new XmlElementEventHandler(DoUnknownElement); SimpleDemo sd = (SimpleDemo)ser.Deserialize(rdr); rdr.Close(); Console.WriteLine("KmStand: {0}", sd.KmStand); Console.Write("\nPress a key ...."); Console.ReadLine(); } Listing 3.15: Eintragen der Ereignisbehandlung

36

Zusammenfassung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Hier das Ergebnis des Programmablaufs:

Abbildung 3.3: Die unbekannten XML-Elemente werden gemeldet.

3.2 Zusammenfassung Serialisierung nach XML ist mit dem .NET-Framework auf einfache Art und Weise möglich, wobei Sie jederzeit in diesen Prozess eingreifen und die Serialisierung der Objektinstanzen genauer steuern können.

37

.NET

Essentials

4

Document Object Model

4.1 Streaming oder DOM? Während sich der Zugriff auf XML-Dateien per Streaming durch einen geringen Verbrauch an Ressourcen auszeichnet, besitzt diese Methode doch den einen oder anderen Nachteil in bestimmten Situationen. Wie Sie im Kapitel über XmlReader und XmlWriter (und den davon abgeleiteten Klassen) nachlesen können, ist nur ein vorwärtsgerichtetes Arbeiten möglich. Dies bedeutet, dass die Informationen in der XML-Datei in der Reihenfolge gelesen werden müssen, in der diese im Dokument auftauchen, während beim Schreiben bereits die endgültige Reihenfolge der Knoten feststehen muss. Besonders schwierig wird der Zugriff bei Zeigern (z.B. IDREF) innerhalb der Datei oder dem nachträglichen Einfügen von Knoten. Aus diesem Grund ist auch bei .NET die Bearbeitung von XML-Dateien über das Document Object Model möglich. Hier wird der komplette Dokumentbaum im Speicher aufgebaut, wobei jeder Knoten einer Instanz eines Nachfahren von XmlNode entspricht.

IDREF

Dieses Kapitel befasst sich mit den grundlegenden Möglichkeiten des .NET-Frameworks zur Bearbeitung von DOM-Strukturen. Die Methoden zur Selektion von Untermengen des Dokumentenbaumes mit Hilfe von XPath-Abfragen werden dann im nächsten Kapitel nach einer kurzen Einführung in das Thema XPath behandelt.

4.2 XML-Dokumente laden und lesen Laden bestehender Dokumente Das Laden bestehender Dokumente geschieht über eine Instanz der Klasse XmlDocument und deren Methode Load. Nach der Erzeugung der Instanz wird also nur die Methode Load benutzt, um dem Dokument gleich einen Inhalt zuzuweisen.

XmlDocument

// erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden einer bestehenden datei thedoc.Load(@"h:\wetter.xml"); // hier erfolgt die bearbeitung der Datei Listing 4.1: Das Laden von bestehenden XML-Dokumenten

39

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Diese Methode ist mehrfach überladen. So können Sie anstelle des Dateinamens auch bei Bedarf einen URL oder eine Instanz eines XmlReaders angeben. LoadXml

Falls die XML-Daten bereits in Form eines Strings vorliegen, kann dieser benutzt werden, um den Inhalt des Dokumentes festzulegen. Dafür existiert die Methode LoadXml, die als Parameter einen String erwartet: // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // stringinhalt für XML, hier direkte zuweisung string s = "Bob"; // laden einer bestehenden Datei thedoc.LoadXml(s); // hier erfolgt die bearbeitung der datei Listing 4.2: Das Laden von bestehenden XML-Dokumenten

Somit sind wir in der Lage, Daten in XML in einem DOM-Objekt zu laden. In den nächsten Abschnitten dieses Kapitels wird es darum gehen, den Inhalt des Dokumentenbaums zu bearbeiten.

Knoten und Elemente XmlNode

Die einzelnen Elemente des Dokumentenbaums bestehen aus Knoten, die hier den einzelnen XML-Elementen entsprechen. Über die Eigenschaft NodeType eines XmlNode lässt sich feststellen, um welchen Knoten es sich handelt (Element, Kommentar, Verarbeitungsanweisung, etc.) und entsprechend kann Ihr Programmcode dann darauf reagieren. Zur Verdeutlichung des nächsten Listings hier nochmals ein Ausschnitt aus der Beispieldatei mit den Wetterdaten:

Algeirs overcast 11 93% E 5 1028 rising

Listing 4.3: Ausschnitt aus der Beispieldatei

40

XML-Dokumente laden und lesen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Um das -Element aus der ersten Stationsmeldung zu lesen, muss der zweite Kindknoten des Dokumentenknotens gelesen werden (der erste wäre die Verarbeitungsanweisung ), davon den ersten Kindknoten (das Element ) und davon wiederum den dritten Knoten. Hier der entsprechende Codeausschnitt dazu: static void SampleChildNodes() { XmlNode node; // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden einer bestehenden Datei thedoc.Load(@"h:\wetter.xml"); // das element holen node = thedoc.ChildNodes[1]; // das element holen node = node.ChildNodes[0].ChildNodes[2]; // inhalt ausgeben Console.WriteLine("Inhalt: {0}", node.OuterXml); } Listing 4.4: Zugriff auf einen bestimmten Elementknoten per DOM

Die Ausgabe erfolgt dann über die Eigenschaft OuterXml, so dass das Tag und die Attribute mit ausgegeben werden. Sind Sie nur am reinen Inhalt interessiert, können Sie statt dessen die Eigenschaft InnerText verwenden.

OuterXml

Abbildung 4.1: Die Ausgabe des Beispielprogramms

Sollte Ihnen der Programmablauf mehr wie ein Hüpfen von Knoten zu Knoten vorkommen, bei dem sich die Reihenfolge der Elemente auf keinen Fall ändern darf, liegen Sie richtig. Im nächsten Kapitel werden Sie mit XPath-Abfragen eine wesentlich bessere Möglichkeit kennenlernen, ein Set aus einzelnen Knoten aus einem Dokumentenbaum zu extrahieren.

41

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Auswählen einzelner Elemente XmlNodelist

Die Auswahl einzelner Knoten oder einer Liste von Knoten geschieht über verschiedene Methoden, die eine Instanz von XmlNode (bzw. einer Ableitung davon) oder eine Auflistung in einer XmlNodeList liefern. Damit lässt sich die Baumstruktur bequem durchsuchen und filtern. Das folgende Beispiel liefert alle location-Elemente der Datei und gibt diese Auflistung in einer Schleife aus, um aufzulisten, für welche Stationen Wetterdaten vorliegen. using System; using System.XML; namespace DOMSamples { class ReadXML { static void Main(string[] args) { // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden einer bestehenden Datei thedoc.Load(@"h:\wetter.xml"); // auslesen aller location-tags XmlNodeList nl = thedoc.GetElementsByTagName("location"); // auflistung in schleife ausgeben foreach(XmlNode node in nl) { Console.WriteLine("Station location: {0}", node.InnerText); } Console.WriteLine("Total # of stations: {0}", nl.Count); } } } Listing 4.5: Auslesen aller benannten Tags eines XML-Dokumentes

GetElementsByID

42

Zusätzlich existiert noch eine Methode GetElementsByID, mit der alle Elemente mit einem bestimmten ID-Attribut zurückgeliefert werden. Dafür muss allerdings ein Schema oder eine DTD zur Validierung vorhanden sein, da in .NET im Document Object Model keine ID-Attribute vorgesehen sind, wenn diese nicht explizit über eine DTD oder ein

XML-Dokumente laden und lesen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Schema definiert werden. Mehr dazu im Kapitel über die Validierung von XML-Dokumenten.

Zugriff auf Attribute Für den Zugriff auf eventuell vorhandene Attribute eines Elementes benutzen Sie zuerst die Eigenschaft Count der Auflistung Attributes des Knotens, um zu testen, ob diese einen Wert größer als 0 enthält.

Attributes und XmlAttribute Collection

Ist dies der Fall, liefert die Auflistung Attributes eine XmlAttribute Collection, deren Elemente aus XmlNodes bestehen. Damit lassen sich die einzelnen Unterelemente eines Knotens bequem nach vorhandenen Attributen durchsuchen, wie das nächste Beispiel zeigt. Hier wird für das erste station-Element in der Beispieldatei jeweils ausgegeben, ob Attribute vorhanden sind, und deren Namen und Wert. Auch hier wird aus Platzgründen wieder nur der Code für die Klasse aufgelistet, der Aufruf in einer Testklasse sollte aber kein Problem darstellen. // ausgabe von attributen aus xml-elementen static void SampleAttributes() { XmlNode node; // erzeugen eines neuen XML-dokumentes XmlDocument thedoc = new XmlDocument(); // laden einer bestehenden datei thedoc.Load(@"h:\wetter.xml"); // das erste element holen node = thedoc.ChildNodes[1].ChildNodes[0]; // durch alle kindknoten des elementes laufen foreach(XmlNode n in node.ChildNodes) { // haben wir attribute? if(n.Attributes.Count > 0) { // dann durch alle attribute laufen foreach(XmlNode a in n.Attributes) { Console.WriteLine("Element besitzt" + " Attribute:", n.Name); Console.WriteLine(" {0} = {1}", a.Name, a.Value); } } else // keine attribute, elementname ausgeben Console.WriteLine("Element hat keine" +

43

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

" Attribute.", n.Name); } } Listing 4.6: Ausgabe der Attribute und ihrer Werte

4.3 Ändern von XML-Dokumenten per DOM Hinzufügen von neuen Elementen Das programmgesteuerte Hinzufügen neuer Elemente (Knoten) in die DOM-Struktur erlaubt den freien Auf- und Umbau eines XML-Dokumentes. Für jeden möglichen Elementtyp existiert in der XmlDocumentKlasse eine eigene Methode, da die Konstruktoren nicht direkt instantiierbar sind, da Elemente immer zu einem bestimmten Dokument gehören. Das Beispiel unten erzeugt zuerst ein neues XML-Dokument und füllt dies dann durch die entsprechenden Methoden-Aufrufe mit Inhalten. Zuletzt wird durch das Speichern nach Console.Out der Inhalt im Konsolenfenster ausgegeben. static void SampleNewContent() { XmlNode node1; XmlNode node2; // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // ein kommentarelement erzeugen thedoc.AppendChild(thedoc.CreateComment( "demo fuer .net-essentials")); // ein -element erzeugen XmlElement elem = thedoc.CreateElement("weatherdata"); thedoc.AppendChild(elem); // ein station-element mit attribut erzeugen node1 = elem.AppendChild(thedoc.CreateElement("station")); (node1 as XmlElement).SetAttribute("id", "Irgendwo"); // einfuegen von text funktioniert // ueber die Methode "CreateTextNode" node2 = node1.AppendChild(thedoc.CreateElement("weather")); node2.AppendChild(thedoc.CreateTextNode("sunny"));

44

Ändern von XML-Dokumenten per DOM ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

thedoc.Save(Console.Out); } Listing 4.7: Erzeugen von neuen Elementen per DOM

Hier die Ausgabe der Methode:

Abbildung 4.2: Die Ausgabe der neu erzeugten XML-Struktur

Löschen von Elementen Um ein Element aus der Struktur zu entfernen, existiert die Methode RemoveChild, die als Parameter den zu entfernenden Unterknoten erwartet. Als Erweiterung zum DOM durch Microsoft existiert noch eine Methode RemoveAll, mit der sich in einem Aufruf alle Unterelemente und Attribute eines Knotens entfernen lassen.

RemoveChild, RemoveAll

Der Aufruf der Methode SampleDeleteElements aus dem folgenden Beispiel entfernt aus allen -Elementen den Unterknoten . static void ProcessElementDelete(XmlNode theNode) { foreach(XmlNode n in theNode.ChildNodes) { if(n.HasChildNodes) ProcessElementDelete(n); } if(theNode.LocalName == "airtrend") { Console.WriteLine("Deleting a node ...."); theNode.ParentNode.RemoveChild(theNode); } } static void SampleDeleteElements() { // erzeugen eines neuen XML-Dokumentes

45

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

XmlDocument thedoc = new XmlDocument(); // laden einer bestehenden Datei thedoc.Load(@"h:\miniwetter.xml"); // hier erfolgt die bearbeitung ProcessElementDelete(thedoc); // geänderte datei rausschreiben thedoc.Save("neuwetter.xml"); } Listing 4.8: Entfernen von Elementen aus der DOM-Struktur

Bearbeiten von Attributen CreateAttribute

Die Behandlung von Attributen erfolgt auf eine etwas abweichende Art und Weise, da die Methode CreateAttribute ein Mitglied der XmlDocument-Klasse ist. Um also ein neues Attribut zu erzeugen, während Sie nur auf den aktuellen Knoten Zugriff haben, gehen Sie folgendermaßen vor:

왘 Feststellen des Dokumentes, das diesen Knoten besitzt. Dazu hat jeder Knoten eine Eigenschaft OwnerDocument. 왘 Aufrufen der Methode CreateAttribute der Dokumentklasse und Übergeben von Name und eventuell Namensraumpräfixen. 왘 Setzen des Wertes für das Attribut. 왘 Hinzufügen des neuen Attributes an die Attribute-Auflistung des aktuellen Knoten. Die beiden nachfolgenden Methoden zeigen einen rekursiven Durchlauf durch ein XML-Dokument, bei dem für jeden Stationsknoten der Name der Station aus dem Unterknoten gelesen und bei als ID-Attribut eingetragen wird. Danach wird die neue Struktur unter einem anderen Namen gespeichert. Damit die Ausgabe leichter lesbar ist, wird hier eine Datei verwendet, die nur zwei Einträge für Wetterstationen besitzt und die den Namen miniwetter.xml trägt. // diese methode wird für jeden knoten aufgerufen static void ProcessElementChildren(XmlNode theNode) { Console.WriteLine("Processing ....", theNode.Name); // id bei station eintragen

46

Ändern von XML-Dokumenten per DOM ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

if(theNode.LocalName == "location") { // neues attribut ueber dokument erzeugen XmlAttribute attr = theNode.OwnerDocument.CreateAttribute("id"); // attributwert setzen auf den inhalt // des aktuellen knotens (location) attr.Value = theNode.InnerText; // an die auflistung der attribute anhängen theNode.ParentNode.Attributes.Append(attr); } // alle eventuellen unterknoten abarbeiten foreach(XmlNode n in theNode.ChildNodes) { if(n.HasChildNodes) ProcessElementChildren(n); } }

// hauptmethode static void SampleRecurseElements() { // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden einer bestehenden Datei thedoc.Load(@"h:\miniwetter.xml"); // hier erfolgt die bearbeitung ProcessElementChildren(thedoc); // geänderte datei rausschreiben thedoc.Save("neuwetter.xml"); } Listing 4.9: Ändern einer DOM-Struktur

Soll ein Attribut eines Elementes gelöscht werden, geschieht dies auf sehr einfache Weise durch den Aufruf der Methode RemoveAttribute der Klasse XmlElement, mit der ein benanntes Attribut aus einem XMLElement gelöscht werden kann. Da dieser Vorgang nicht sonderlich geheimnisvoll ist, sei an dieser Stelle auf ein weiteres Listing verzichtet.

RemoveAttribute

Eine andere Alternative ist die Nutzung der hilfreichen Methode SetAttribute der Klasse XmlElement. Bitte beachten Sie, dass aus Gründen

47

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

des Zeilenumbruchs die beiden Zeilen 05 und 06 getrennt wurden, im Code ist dies eine einzige Zeile. 01 if(theNode.LocalName == "station") 02 { 03 XmlElement elem = ((XmlElement)theNode); 04 elem.SetAttribute("id", 05 elem.GetElementsByTagName("location") 06 .Item(0).InnerText); 07 } Listing 4.10: Alternative Erzeugung von Attributen

Mit dieser Methode lässt sich auch der Inhalt eines bereits bestehenden Attributes wieder ändern.

4.4 Ereignisbehandlung Die Klasse XmlDocument beinhaltet auch eine Reihe von Ereignissen, in die sich Ihr Code mit einer eigenen Ereignisbehandlungsroutine einhängen kann. Auf diese Weise kann nicht nur mitverfolgt werden, ob ein Knoten geändert, eingefügt oder gelöscht wurde. Über einen zweiten Satz von Ereignissen, der vor der eigentlichen Aktion ausgelöst wird, lässt sich die Durchführung der Änderung sogar bei Bedarf abbrechen.

t

Ereignis

Beschreibung

NodeChanged

Tritt auf, wenn ein Knoten in der DOM-Struktur geändert wurde

NodeChanging

Tritt auf, bevor ein Knoten in der DOM-Struktur geändert wird (Auslösen einer Exception bricht die Aktion ab)

NodeInserted

Tritt auf, wenn ein Knoten in der DOM-Struktur eingefügt wurde

NodeInserting

Tritt auf, bevor ein Knoten in der DOM-Struktur eingefügt wird (Auslösen einer Exception bricht die Aktion ab)

NodeRemoved

Tritt auf, wenn ein Knoten in der DOM-Struktur gelöscht wurde

NodeRemoving

Tritt auf, bevor ein Knoten in der DOM-Struktur gelöscht wird (Auslösen einer Exception bricht die Aktion ab)

Tabelle 4.1: Mögliche Ereignisse der XmlDocument-Klasse

Um auf eines dieser Ereignisse zu reagieren, muss in der Klasse ein EventHandler programmiert und dem Delegate für das Ereignis mitgeteilt werden. Das folgende Listing zeigt eine Klasse, die beim Löschen eines Knotens eine entsprechende Meldung ausgibt. // klasse zur ereignisbehandlung class EventDemo

48

Ereignisbehandlung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

{ public void DemoActions() { XmlDocument thedoc = new XmlDocument(); thedoc.NodeRemoved += new XmlNodeChangedEventHandler(this.OnDelete); thedoc.Load(@"h:\miniwetter.xml"); try { thedoc.ChildNodes[1].ChildNodes[0].RemoveAll(); } catch (Exception e) { Console.WriteLine(e.Message); } thedoc.Save(Console.Out); Console.Write("Press a key ...."); Console.ReadLine(); } // knoten wurde gelöscht public void OnDelete(Object src, XmlNodeChangedEventArgs args) { Console.Write("Löschereginis: {1}", args.Node.Name, args.Action.ToString()); if (args.Node.Value != null) Console.WriteLine(" Wert = {0}", args.Node.Value); else Console.WriteLine(""); } } Listing 4.11: Abfangen von Löschereignissen im Dokument

Analog dazu funktioniert die Behandlung der NodeChanged- und Node Inserted-Ereignisse. Interessant ist aber die Möglichkeit, in diesen Prozess einzugreifen und bei Bedarf durch das Auslösen einer Exception die Aktion abzubrechen. Dazu dienen die Ereignisse, deren Namen jeweils auf »...ing« enden und die vielleicht besser als Anforderung bezeichnet werden.

NodeChanged, NodeInserted

49

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Im folgenden Beispiel soll verhindert werden, dass für eine Station der -Knoten gelöscht wird, alle anderen Unterknoten von sollen gelöscht werden können. Denken Sie daran, beim Löschen von Unterelementen in einer Schleife nicht mit foreach zu arbeiten, da hier das Set der zu durchlaufenden Elemente nur einmal berechnet wird und dann nicht mehr geändert werden darf. Zusätzlich muss die Schleife von »hinten nach vorne« durchlaufen werden, damit nach dem Löschen wegfallende Elemente die Reihenfolge nicht ducheinander bringen. Deshalb die auf den ersten Blick etwas merkwürdig aussehende Schleifenkonstruktion. Wollen Sie in einem Rutsch wirklich alle Unterknoten eines Knotens entfernen, benutzen Sie dazu am besten die Methode RemoveAll der Klasse XmlNode. Diese stellt eine nützliche Erweiterung des DOM-Standards durch Microsoft dar. // beispiel zum abfangen einer löschaktion im DOM class EventDemo { public void DemoActions() { XmlNode node; XmlDocument thedoc = new XmlDocument(); // eintragen der eventhandler thedoc.NodeRemoved += new XmlNodeChangedEventHandler(this.OnDelete); thedoc.NodeRemoving += new XmlNodeChangedEventHandler(this.OnDeleteReq); thedoc.Load(@"h:\miniwetter.xml"); // die erste station in der wetterdatei node = thedoc.ChildNodes[1].ChildNodes[0]; // alle unterelemente löschen for(int i = node.ChildNodes.Count-1; i >= 0; i--) { try { node.RemoveChild(node.ChildNodes[i]); } catch (Exception e) { Console.WriteLine(e.Message); } }

50

Ereignisbehandlung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

// den inhalt der XML-Strukur zur // kontrolle ausgeben thedoc.Save(Console.Out); Console.Write("Press a key ...."); Console.ReadLine(); } // ab hier die ereignisbehandlungsroutinen // knoten wurde gelöscht public void OnDelete(Object src, XmlNodeChangedEventArgs args) { Console.Write("Löschereignis: {1}", args.Node.Name, args.Action.ToString()); if (args.Node.Value != null) Console.WriteLine(" Wert = {0}", args.Node.Value); else Console.WriteLine(""); } // knoten soll gelöscht werden, extracheck public void OnDeleteReq(Object src, XmlNodeChangedEventArgs args) { Console.Write("Löschanfrage: {1}", args.Node.Name, args.Action.ToString()); if (args.Node.Value != null) Console.WriteLine(" Wert = {0}", args.Node.Value); else Console.WriteLine(""); if(args.Node.LocalName == "location") { throw new XmlException("Location darf nicht " + "gelöscht werden!", null); } } } Listing 4.12: Abbrechen von Änderungen im DOM

Nach der Exception befindet sich die XML-Struktur in der XmlDocumentInstanz in einem stabilen Zustand wie vor der Aktion. Wäre im Code statt der Schleife durch die Unterelemente die Methode RemoveAll aufgerufen worden, hätte die Exception beim Element dafür gesorgt, dass gar keines der Unterelemente gelöscht worden wäre, da

51

.NET

4 Document Object Model ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

dann der komplette Aufruf von RemoveAll zurückgerollt worden wäre. Dies lässt sich bei Bedarf gezielt dafür einsetzen, komplette Knoten zu schützen, aber nur ein Unterelement zu prüfen.

4.5 Zusammenfassung In diesem Kapitel haben Sie eine Übersicht über die verschiedenen Klassen und Methoden des DOM im .NET-Framework erhalten. Vergleicht man die Anforderungen und Möglichkeiten mit denen im Kapitel über die Streaming-Klassen XmlReader und XmlWriter, dann lässt sich auch einordnen, wann am besten welche Methode zum Einsatz kommt. Sollten Sie eine große Anzahl von Dokumenten in einer Art Stapelbetrieb verarbeiten müssen oder benötigen Sie aus einem umfangreichen XML-Dokument nur eine kleine Anzahl von Elementen (z.B. um aus einer Liste von Teilen die Gewichte für den Versand zu berechnen), dann ist der Streaming-Ansatz wesentlich ressourcenschonender. Sollten Sie aber die volle Kontrolle über die XML-Struktur benötigen oder beim Aufbau im Speicher noch nicht die endgültige Reihenfolge der Elemente wissen (weil diese z.B. erst während der Laufzeit Ihres Programms erzeugt werden), dann ist es besser, mit dem DOM zu arbeiten, auch wenn dadurch der komplette DOM-Baum im Speicher aufgebaut wird.

52

.NET

Essentials

5

XPath-Abfragen

5.1 Warum XPath? Wie Sie in den vorherigen Kapiteln erfahren haben, bietet das .NETFramework eine ganze Reihe von Möglichkeiten, um XML-Strukturen zu lesen und zu schreiben. Allerdings ist der Weg zu einem bestimmten Knoten bzw. Element mehr oder weniger steinig. Das reine Abzählen von Unterknoten funktioniert beispielsweise nur dann, wenn Sie sich sicher sind, dass die Reihenfolge der Knoten nicht verändert wurde. Wie Sie dies sicherstellen und auch bestimmte Elemente zwingend erforderlich machen, erfahren Sie im Kapitel über die Validierung von XML-Dateien. Hier soll es aber zuerst darum gehen, eine sehr mächtige und flexible Möglichkeit zur Suche nach einem oder mehreren Knoten in einer DOMStruktur vorzustellen, nämlich den XPath-Standard. XPath ist eine Entwicklung des World Wide Web Consortiums (W3C) zur Lokalisierung von Knoten in XML-Strukturen, die normalerweise nicht allein genutzt wird, sondern dazu dient, zusammen mit anderen Standards benutzt zu werden. So bestehen zum Beispiel die Suchausdrücke der Schablonen für die Transformation von XML aus XPath-Ausdrücken. XPath operiert auf der abstrakten, logischen Struktur eines XML-Dokuments, nicht auf seiner äußerlichen Syntax. Seinen Namen erhält XPath durch die Verwendung einer auch in URLs genutzten Pfad-Notation (path), mit der sich durch die hierarchische Struktur eines XML-Dokuments navigieren lässt. Für eine erfolgreiche Arbeit mit XML ist daher eine eingehende Beschäftigung mit XPath unabdingbar. Dieses Kapitel kann aus Platzgründen nur eine kurze Einführung bieten, für die detailliertere Beschäftigung sei auf die Homepage für XPath unter http://www.w3.org/TR/ xpath.html verwiesen.

Das Prinzip von XPath

5.2 Grundlagen XPath-Ausdrücke In der einfachsten Form beschreibt eine XPath-Abfrage den durch Schrägstriche getrennten Weg in der hierarchischen XML-Struktur bis zum Zielknoten. So liefert der XPath-Ausdruck weatherdata/station/temp alle Knoten unterhalb von , wobei dieses Element ein Unterknoten von sein muss.

53

.NET

5 XPath-Abfragen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Die folgende Abbildung zeigt die Ausgabe, wenn für jeden Knoten die Eigenschaft OuterXml ausgegeben wird.

Abbildung 5.1: Ausgabe aller -Knoten im XML-Baum

Der |-Operator

XPath-Ausdrücke lassen sich mit Hilfe des Operators "|" (in der Bedeutung von »oder«) verbinden. So liefert der Ausdruck /weatherdata/station/location | /weatherdata/station/temp beispielsweise einen Match für entweder das - oder das -Element. Da der XPath-Parser zuerst die Geschwisterknoten der gleichen Ebene durchsucht, erhalten Sie so auf einfachste Art eine Auflistung der Standorte mit der jeweiligen Temperatur. Hier das Listing und die Ausgabe des Programms dazu: // kombinieren von XPath-Abfragen static void SampleSelectOr() { // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden der xml-datei thedoc.Load(@"h:\miniwetter.xml"); // abfragen per XPath XmlNodeList nl = thedoc.SelectNodes( "/weatherdata/station/location | " + /weatherdata/station/temp"); foreach(XmlNode n in nl) { Console.WriteLine(n.OuterXml); } } Listing 5.1: Kombination zweiter XPath-Ausdrücke

54

Grundlagen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Abbildung 5.2: Ausgabe der kombinierten XPath-Abfrage

Der Kontextknoten Wird eine XPath-Abfrage beispielsweise nach dem Knoten durchgeführt (name), hängt es davon ab, von wo die Suche gestartet wird. Ein Beginn an der Wurzel des Dokumentes liefert unter Umständen ein komplett anderes Ergebnis als der Beginn an einem Unterknoten innerhalb der XML-Struktur. Daher verwendet XPath das Konzept eines sogenannten »Kontextknotens«. Dieser Kontextknoten ist der aktuelle Knoten, an dem die Suche gestartet wird (bei der Verwendung der Methode SelectNodes beispielsweise der XmlNode, für den die Methode aufgerufen wird).

Die Abkürzung für den Kontextknoten ist der . (punkt)

Achsen Für die Durchführung einer XPath-Abfrage kann eine bestimmte Richtung definiert werden, die sog. Achse. Auch in einem hierarchischen Dateisystem beispielsweise kann über die Notation .. eine Ebene höher gewechselt werden, um »nach oben« zu suchen. Bei der normalen Angabe wie /weatherdata/station/location ist die Achse immer child, also jeweils ein Unterknoten. Dies ist die Default-Achse, muss daher auch nicht angegeben werden. Wird hingegen eine Achse angegen, trennt man diese vom Rest der Abfrage durch einen doppelten Doppelpunkt (::). Einige Achsen werden (wie child) sehr häufig benötigt und besitzen eine eigene Abkürzung. Die wichtigsten Achsen zeigt die nachfolgende Tabelle. Startpunkt ist immer der Kontextknoten. Achse

Bedeutung

child

Default, ein Nachkomme des Kontextknotens

self

Der Kontextknoten (Abkürzung .)

descendant

Irgendein Nachkomme des Kontextknotens, also z.B. auch ein Nachkomme über zwei oder drei Ebenen

descedant-or-self

Wie oben, allerdings unter Einbeziehung des Kontextknotens (Abkürzung //)

parent

Elternknoten des Kontextknotens (Abkürzung ..)

t

Tabelle 5.1: Die wichtigsten Achsen für XPath-Ausdrücke

55

.NET

5 XPath-Abfragen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Achse

Bedeutung

following

Alle nach dem Kontextknoten folgenden Knoten

preceding

Alle vor dem Kontextknoten vorhandenen Knoten

attribute

Attribut des angegebenen Knotens (Abkürzung @)

Tabelle 5.1: Die wichtigsten Achsen für XPath-Ausdrücke (Fortsetzung)

Eine vollständige Liste der möglichen Achsen finden Sie auf der XPathHomepage im Web unter dem URL www.w3.org/TR/xpath.html . Verweis auf Elternknoten mit ..

Um beispielsweise alle -Elemente zu selektieren, egal zu welchem Elternknoten diese gehören, kann der Ausdruck //temp benutzt werden. Der Ausdruck //location/.. liefert alle Elternknoten (in diesem Falle ), die ein -Element als Kindknoten besitzen. Somit sind sehr leicht Abfragen in verschiedenen »Richtungen« möglich, die durch die Auswahl des Kontextknotens noch zusätzlich beeinflusst werden können. Für eine noch leichtere Abfrage fehlen jetzt nur noch Möglichkeiten, einen Vergleich durchzuführen, um nur ein bestimmtes Subset von passenden Knoten auszuwählen. Dies geschieht bei XPath mit Hilfe von so genannten Prädikaten.

Prädikate Prädikate entsprechen in etwa der WHERE-Klausel bei einer SQL-Abfrage, da sie das zurückgelieferte Set von Knoten durch Bedingungen noch weiter filtern können. Diese Prädikate werden an der gewünschten Stelle hinter das jeweilige Hierarchieelement in eckigen Klammern angefügt. Klingt kompliziert, ist es aber nicht. Hier ein Beispiel: //station[location = "Athens"] liefert das -Element, welches ein Unterlement besitzt, das den Inhalt Athens besitzt. Prüfung auf vorhandene Attribute

Der Prädikatwert muss nicht unbedingt einen Vergleich enthalten. Wird nur ein Unterelement oder ein Attribut angegeben, dann wird auf das Vorhandensein dieses Elementes geprüft. Der nachfolgende Ausdruck liefert in der XmlNodeList alle -Knoten, die als Elternknoten ein -Element besitzen, das wiederum ein Attribut "status" enthält. /weatherdata/station[@status]/location Wird mehr als ein Prädikat angegeben, was durchaus zulässig ist, besitzt die Reihenfolge dieser Prädikate eine entscheidende Bedeutung. Betrachten wir dazu die beiden folgenden XPath-Ausdrücke: 1. /weatherdata/station[2][@status] 2. /weatherdata/station[@status][2]

56

Grundlagen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Für den Ausdruck Nr. 1 ist das Ergebnis der zweite -Knoten unterhalb von , falls dieser über ein Attribut (wie Sie an der Abkürzung @ für die Attribut-Achse sehen) mit dem Namen status verfügt. Die Knotenliste wäre hier leer, da der zweite Knoten in unserer Beispieldatei wetter.xml (für die Station in Amsterdam) nicht über das entsprechende Attribut verfügt. Der Ausdruck Nr. 2 liefert dagegen zuerst die Liste der Stationen mit einem Attribut status und wählt anschließend davon den zweiten Knoten aus. Im Falle der Beispieldatei enthält die Ergebnismenge eine Ausgabe:

Bukarest light rain 10 49% W 3 1013 raising

Listing 5.2: Die Ergebnismenge der zweiten Abfrage

Zur Kombination von Ausdrücken innerhalb der Prädikate können Sie mit den drei booleschen Operatoren and, or und not arbeiten (case-sensitive) bzw. Teilausdrücke in Klammern gruppieren. Denken Sie bei den relationalen Operatoren daran, das Zeichen "" entsprechend durch ein ">" zu quoten, schließlich befinden wir uns innerhalb der XML-Grenzen. Mehr dazu und zu den weiteren Möglichkeiten der Prädikate finden Sie in der Online-Dokumentation von .NET oder auf den Webseiten des W3C.

Prädikat-Operatoren

Als letzten Bestandteil der Einführung in die XPath-Sprache fehlen nun nur noch die Funktionen, mit deren Hilfe noch flexiblere Abfragen möglich sind.

Funktionen Funktionen innerhalb von XPath-Ausdrücken können einen der vier folgenden Datentypen zurückliefern: Boolsche Werte (true oder false), Zahlenwerte, Zeichenketten oder eine Knotenmenge. Wenn eine Funktion einen anderen Datentyp als eine Knotenmenge liefert, können natürlich die nachfolgend besprochenen Methoden SelectNodes und SelectSingleNode nicht benutzt werden. Verwenden Sie dafür die Methoden des XPathNavigators, der weiter unten vorgestellt wird.

Funktionstypen

57

.NET

5 XPath-Abfragen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Mit Hilfe dieser Funktionen ist es sehr einfach möglich, Code zu vermeiden und die Arbeit einfach in einen XPath-Ausdruck auszulagern. Damit Sie die folgenden Beispiele nachvollziehen können, sollten Sie zuerst die Abschnitte über SelectNodes und den XPathNavigator lesen und dann hierher zurückblättern. Aus der Sicht des Autors erschien es an dieser Stelle sinnvoller, die XPath-Einführung nicht zu zerreißen, sondern linear aufzubauen. Sobald eine Funktion kein Set von Knoten mehr liefert, sind die üblichen Selektierungsmethoden (wie SelectNodes oder Select) nicht mehr anwendbar. Benutzen Sie statt dessen die Methode Evaluate der Klasse XPathNavigator, welche als Rückgabetyp object besitzt und dementsprechend per Unboxing oder Konvertierung gewandelt werden kann. Evaluate-Funktion

Eine der einfachsten Funktionen ist count(), die einfach die Anzahl der auf den Ausdruck passenden Knoten liefert. Damit lässt sich die Anzahl der in der Beispieldatei enthaltenenen Stationsmeldungen mit einer einzigen Zeile Code bestimmen. int nCount = (int)xnav.Evaluate("count(//station)"); Listing 5.3: Zählen von Knoten mit XPath-Funktionen

Es existiert eine recht beachtliche Liste von nützlichen Funktionen, wobei sich Microsoft hier an den XPath-Standard hält. Anstatt diese Liste einfach zu wiederholen, sei auf die Online-Hilfe von Visual Studio.NET verwiesen, in der Sie unter dem Indexbegriff XPath, Funktionen die entsprechende Liste finden. Nachfolgend noch einige Beispiele, die ein paar der unterstützten Funktionen demonstrieren:

t

Ausgabe aller Temperaturknoten, aber nur der Textinhalt des Elements: /weatherdata/station/temp/text() Ausgabe aller Stationen mit "P": /weatherdata/station[ starts-with(location,"P")]/location Ausgabe der Luftfeuchtigkeit für alle Stationen, die eine Temperatur von mehr als 10° melden: /weatherdata/station/temp[text() > 10]/../humidity Alle Stationsdaten mit steigendem Luftdruck und mehr als 10°: /weatherdata/station[(temp > 10) and (airtrend = "rising")]

58

SelectNodes und SelectSingleNode ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

5.3 SelectNodes und SelectSingleNode Mit der Methode SelectNodes lässt sich eine geladene DOM-Struktur mit Hilfe einer XPath-Abfrage durchsuchen und als XmlNodelist-Auflistung zurückgeben.

SelectNodes

Das folgende Beispiel liefert alle -Elemente unterhalb von /weatherdata/station und berechnet anschließend die Durchschnittstemperatur der Wettermeldungen. static void SampleSelectNodes() { double sum = 0.0; int count = 0; // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden der xml-datei thedoc.Load(@"h:\wetter.xml"); // abfragen per XPath XmlNodeList nl = thedoc.SelectNodes("/weatherdata/station/temp"); // durchlaufen der nodelist foreach(XmlNode n in nl) { sum += double.Parse(n.InnerText); count++; } Console.WriteLine("{0} Werte. Schnitt = {1:F2} Grad", count, sum / count); } Listing 5.4: Auslesen von Knoten mit XPath-Abfragen

Damit ist auf einfache Art und Weise nahezu ohne Programmieraufwand das Durchsuchen von XML-Strukturen nach bestimmten Kritierien möglich. Soll ein ganz bestimmter Knoten (z.B. anhand einer ID) gefunden werden, kann dazu die Methode SelectSingleNode benutzt werden. So lassen sich mit der folgende Klasse die kompletten XML-Elemente für einen Standort (also das -Element) abrufen, in dem der Name des Standortes eingegeben wird. Wird kein passender Knoten gefunden, liefert die Methode eine Null-Referenz, auf die gestestet werden kann.

SelectSingleNode

59

.NET

5 XPath-Abfragen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Im Beispiel wird über ein XPath-Prädikat (s.o. bei den Grundlagen) der Name des Standortes gesucht, aber das -Element zurückgeliefert. // suchen eines einzelnen xml-nodes // per SelectSingleNode static void SampleSelectSingleNode() { string location; // der gesuchte name string xpath; // der XPath-Ausdruck // erzeugen eines neuen XML-Dokumentes XmlDocument thedoc = new XmlDocument(); // laden der xml-datei thedoc.Load(@"h:\wetter.xml"); // abfrageschleife do { Console.Write("Standort eingeben : "); location = Console.ReadLine(); // bei leerer eingabe schleife verlassen if(location.Length == 0) break; // abfrage durchführen xpath = "/weatherdata/station[location = \"" + location + "\"]"; XmlNode node = thedoc.SelectSingleNode(xpath); // wenn objektreferenz, dann ergebnis ausgeben if( node != null) Console.WriteLine(node.OuterXml); else Console.WriteLine( "Ort nicht in Daten enthalten!"); } while(true); } Listing 5.5: Suchen eines einzelnen Knotens per XPath

60

Nutzung des XPathNavigators ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

5.4 Nutzung des XPathNavigators Anstelle der beiden Methoden der XmlDocument-Klasse kann man für eine bessere Leistung bei der Verarbeitung von XPath-Abfragen auch eine Klasse nutzen, die eine Implementierung der Schnittstelle IXPathNavigable bereitstellt. Diese Klasse ist für die Nutzung innerhalb der Transformation mit XSLT optimiert, kann aber auch als schnelle und optimierte Methode zum Suchen mit XPath in normalen Dokumenten verwendet werden. Die Klasse XPathNavigator selbst ist eine abstrakte Klasse, für eigene Zwecke (z.B. das Bereitstellen von XPath-Abfragen für ein hierarchisches Dateisystem) lässt sich eine entsprechende Ableitung von XPathNavigator implementieren. In der derzeitigen Version des .NET-Frameworks sind das die beiden Klassen XPathDocument und XmlNode. Hier wird der Navigator über einen Aufruf der Methode Create Navigator erzeugt, wie die nachfolgenden Beispiele noch im Detail zeigen werden.

XPathDocument

Hier der Code zur Erzeugung einer XPathNavigator-Instanz, in dem in ein XPathDocument eine bestehende XML-Datei geladen wird.

XPathNavigator

// erzeugen des XML-Dokumentes XPathDocument thedoc = new XPathDocument(@"h:\wetter.xml"); // erstellen der XPathNavigator-Instanz XPathNavigator xnav = thedoc.CreateNavigator();

Der Konstruktor von XPathDocument ist mehrfach überladen, so dass Sie natürlich auch auf andere Art und Weise XML-Daten übergeben können. Sie können eine Instanz des Dokumentes entweder als Stream, als UNC-Pfad, TextReader oder XmlReader erzeugen.

Navigation mit dem XPathNavigator Für die Navigation mit dem XPathNavigator lassen sich die folgenden Methoden einsetzen, die eine direkte Navigation im Vergleich zu den nachfolgend beschriebenen Auswahlmethoden vornehmen. Methode

Beschreibung

Matches

Prüft, ob der aktuelle Knoten dem angegebenen XPath-Ausdruck entspricht. Kann zum Testen einer bestimmten Position verwendet werden.

MoveTo

Springt an die Knotenposition, die den Kontext für den angegebenen XPathNavigator bildet.

MoveToAttribute

Bewegt den Navigator zum angegebenen Attribut, wobei ein Namensraum angegeben werden kann.

t

Tabelle 5.2: Die Navigationsmethoden für den XPathNavigator

61

.NET

5 XPath-Abfragen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Methode

Beschreibung

MoveToFirst

Geht zum ersten Geschwisterknoten des aktuellen Knotens (der erste Knoten auf der gleichen Ebene).

MoveToFirstAttribute

Springt zum ersten Attribut.

MoveToFirstChild

Springt zum ersten Unterknoten des aktuellen Knotens.

MoveToFirstNamespace

Bewegt den Navigator zum ersten Namensraumknoten des aktuellen Knotens.

MoveToId

Springt zu dem Knoten, der ein ID-Attribut mit dem angegebenen Zeichenfolgenwert besitzt.

MoveToNamespace

Bewegt den Navigator zum Namensraumknoten mit dem angegebenen lokalen Namen.

MoveToNext

Geht zum nächsten Geschwisterknoten des aktuellen Knotens (nächster Knoten der aktuellen Ebene).

MoveToNextAttribute

Springt zum nächsten Attribut.

MoveToNextNamespace

Springt zum nächsten Namensraumknoten.

MoveToParent

Bewegt den Navigator zum Elternelement des aktuellen Knotens.

MoveToPrevious

Springt zum vorherigen Geschwisterknoten des aktuellen Knotens.

MoveToRoot

Springt zum Wurzelknoten, zu dem der aktuelle Knoten gehört.

Tabelle 5.2: Die Navigationsmethoden für den XPathNavigator (Fortsetzung)

Auswählen von Knotenmengen mit Select XPathNodeIterator

Mit Hilfe der Methode Select lassen sich XPath-Ausdrücke zur Filterung einer XML-Struktur benutzen, für deren Treffer die Methode dann einen XPathNodeIterator zurückliefert. Die Methode Select liefert eine Iterator-Instanz, die zuerst auf dem Knoten positioniert ist, der als Kontext (und damit Startknoten) für die XPath-Abfrage dient. Daher muss zuerst einmal MoveNext aufgerufen werden, um auf den ersten Knoten des ResultSets zu treffen. Das folgende Listing liefert einen kurzen »Wetterbericht«, in dem über einen XPath-Ausdruck jeweils das - und das Element gesucht und dann mit Hilfe des Iterators in einer Schleife ausgegeben werden. // nutzung von XPathNodeIterator static void SampleXPathNodeIterator() { // erzeugen des XML-Dokumentes XPathDocument thedoc = new XPathDocument(@"h:\wetter.xml");

62

Nutzung des XPathNavigators ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

// erstellen der XPathNavigator-Instanz XPathNavigator xnav = thedoc.CreateNavigator(); XPathNodeIterator iter = xnav.Select("//location | //weather"); while(iter.MoveNext()) { Console.Write("Wetter in {0}: ", iter.Current.Value); iter.MoveNext(); Console.Write("{0}", iter.Current.Value); Console.WriteLine(); } } Listing 5.6: Durchlaufen eines Knotensets mit dem Iterator

Sie können für den Iterator die gleichen Methoden wie für den Navigator verwenden, da die Eigenschaft Current einen auf den aktuellen Knoten positionierten XPathNavigator zurückliefert. Allerdings können Sie diesen Navigator nicht aus dem gelieferten Trefferset herausbewegen. Sie können statt dessen diesen Navigator klonen (über die Methode XPathNavigator.Clone, die einen auf den gleichen Knoten positionierten Klon des für den Aufruf verwendeten Navgators liefert) und dann mit dem Klon aus dem Trefferset herausnavigieren, falls Sie dies benötigen.

Die Eigenschaft Current

Auswerten von Ausdrücken Die Methode Evaluate des XPathNavigators bietet eine sehr flexible Möglichkeit, auch XPath-Ausdrücke auszuwerten, die einen anderen Datentyp als ein Knotenset liefern. In Kombination mit XPath-Funktionen und den Operatoren für XPath-Ausdrücke lassen sich manche Aufgaben fast ohne Code erledigen, indem der XPath-Engine die Arbeit zugeschoben wird. Als Beispiel soll hier die Berechnung der Durchschnittstemperatur aller Wettermeldungen dienen.

Nochmal Evaluate

Im Kapitel über die Nutzung der Streamingklassen XmlReader und XmlWriter finden Sie ein Listing, das die gleiche Aufgabe löst. Hier war allerdings wesentlich mehr Arbeit nötig. Das Abfragen der passenden Tagnamen, die Addition der Werte usw. wurde alles im Code erledigt. Mit Hilfe der Evaluate-Methode verkürzt sich der Code radikal: // berechnungen als xpath-ausdruck static void SampleXPathDocument() { // erzeugen des XML-Dokumentes

63

.NET

5 XPath-Abfragen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

XPathDocument thedoc = new XPathDocument(@"h:\wetter.xml"); // erstellen der XPathNavigator-Instanz XPathNavigator xnav = thedoc.CreateNavigator(); string xpr = "sum(//temp/text()) div count(//temp)"; double avg = (double)xnav.Evaluate(xpr); Console.WriteLine("{0:F2}", avg); } Listing 5.7: Durchschnittsberechnung mit XPath und Evaluate

Liefert eine Auswertung eines XPath-Ausdrucks ein Set von Knoten, dann wird eine XPathNodeIterator-Instanz geliefert (s. voriger Abschnitt), mit der Sie dann die einzelnen Knoten der Ergebnismenge durchlaufen können.

Optimieren durch Kompilieren von Ausdrücken Bei jeder Auswahl von Knoten mit Hilfe eines XPath-Ausdrucks muss der Parser jedesmal wieder die Analyse und Optimierung der Abfrage durchführen, was bei wiederverwendeten Abfragen zu einer Leistungseinbuße führt. Daher besitzen alle entsprechenden Methoden, die einen XPath-Ausdruck akzeptieren, auch eine Überladung für einen kompilierten Ausdruck. Dieser wird durch eine Instsanz der Klasse XPathExpression gebildet. Die Nutzung dieser Funktionalität erfordert nur minimal mehr Code, bietet aber gerade bei komplexeren Ausdrücken eine deutliche Steigerung der Abfrageleistung. XPathExpressions bringen Performance

Hier das Listing aus dem letzten Abschnitt, nur umgewandelt in eine Form, bei der der XPath-Ausdruck zuerst kompiliert und dann verwendet wird. Wie Sie sehen, kein Mehraufwand, aber eine gesteigerte Performance bei komplexen Abfragen (das Konzept ist ähnlich SQL mit ad-hoc queries und stored procedures). // berechnungen als xpath-ausdruck static void SampleXPathDocument() { // erzeugen des XML-Dokumentes XPathDocument thedoc = new XPathDocument(@"h:\wetter.xml"); // erstellen der XPathNavigator-Instanz XPathNavigator xnav = thedoc.CreateNavigator(); // jetzt zuerst kompilieren des ausdrucks

64

Zusammenfassung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

XPathExpression xpr = xnav.Compile( "sum(//temp/text()) " + "div count(//temp)"); double avg = (double)xnav.Evaluate(xpr); Console.WriteLine("{0:F2}", avg); } Listing 5.8: Kompilieren von Ausdrücken steigert die Leistung.

5.5 Zusammenfassung Die Möglichkeit, innerhalb des DOM-Modells mit der XPath-Unterstützung von .NET zu arbeiten, erleichtert die Arbeit erheblich. Viele vorher manuell zu codierende Arbeiten können so an die XPath-Engine abgegeben werden und das Durchsuchen von komplexen XML-Strukturen wird durch die mächtigen XPath-Ausdrücke sehr erleichtert. Im übernächsten Kapitel werden Sie erfahren, wie mit Hilfe dieser Ausdrücke die Transformation von XML-Daten gesteuert werden kann.

65

.NET

Essentials

6

XML-Namensräume

6.1 Einführung Wozu überhaupt Namensräume, wird sich mancher Einsteiger in die XML-Programmierung fragen. Die Antwort ist einfach: wie in jedem Namenssystem zur Vermeidung von Namenskonflikten. Ebenso wie es in einem Dateisystem eine Datei text.txt nur dann zweimal geben kann, wenn sich diese in zwei Verzeichnissen befinden (und damit im Pfadnamen einen anderen "Präfix" besitzen), können Elementnamen ohne Probleme mehrfach verwendet werden, solange sie zwei verschiedenen Namensräumen angehören. Zur Kennzeichnung eines Namensraumes dient ein uniform resource identifier (Details dazu finden Sie im RFC2396, der unter dem URL http://www.ietf.org/rfc/rfc2396.txt zu finden ist), der eine eindeutige Identifikation des Namensraumes darstellt. Obwohl viele NamensraumURIs einem Web-URL ähneln, handelt es sich dabei nicht um einen Link (trotzdem aktivieren viele Firmen unter einem URL-URI oft eine Hinweisseite in HTML, z.B. auch das W3C). Wie der Name schon ausdrückt, geht es hier nur um eine eindeutige Kennzeichnung und da ein Domainname nur einmal vergeben wird, ist eine URL-URI eben einfach eindeutig zu erstellen.

URI und URL

Nachdem die Angabe des URI bei jedem Element dieses Namensraums zu viel Aufwand wäre, existiert jeweils zugeordnet zu einem Namensraum ein Präfix, das durch einen Doppelpunkt vom Elementnamen (dem local name) getrennt wird. Innerhalb eines Elementes ist auch die mehrfache Angabe von Namensräumen möglich.

Präfixe und Namensraum-URIs

Wie das XML-Fragment unten zeigt, lässt sich mit Hilfe von XMLNamensräumen ein Konflikt zwischen den beiden Elementen mit dem lokalen Namen titel auflösen. Ohne die entsprechenden Namensräume wären die beiden Elemente für eine verarbeitende Anwendung auch nicht voneinander zu unterscheiden gewesen.

Alice im Windowslandtitel>

Prof. Dr. Franz Testhuber

Listing 6.1: Ein XML-Fragment mit Namensräumen

67

.NET

6 XML-Namensräume ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

NamensraumDeklaration

Auch bei der Definition eines XML-Schemas sind Namensräume wichtig, da für ein Schema definiert wird, für welchen Namensraum-URI das im Schema definierte Inhaltsmodell gelten soll. Hierfür dient das Attribut targetNamespace des -Elementes. Sehen wir uns dazu im Schema meteo.xsd die Definition des -Elements an:

Listing 6.2: Kopf des meteo-Schemas

Hier wird als Zielnamensraum der URI "urn:meteo-demo" verwendet. Gleichzeitig wird ein Präfix wx für den gleichen Namensraum vereinbart, so dass alle Elemente ohne Präfix und solche mit dem Präfix wx als zu diesem Namensraum gehörig angesehen werden. Zusätzlich wird der Namensraum mit dem URI http://www.w3.org/2001/XMLSchema und dem Präfix xsd deklariert. Dieser enthält das Schema für die XSD-Schemata. Auch hier sollten Sie sich an die beiden Konventionen xsd oder xs für das Schema-Präfix halten, um Ihren programmierenden Kollegen Verwirrung zu ersparen. Für die Feststellung der Eindeutigkeit eines URI sei noch erwähnt, dass der RFC2396 festlegt, dass ein URI dann mit einem anderen URI identisch ist, wenn diese bei einem Zeichenvergleich keinerlei Unterscheide aufweisen. Achten Sie deshalb bei der Übernahme bekannter URIs immer auf die richtige Schreibweise.

t

Für die Beispiele in diesem Kapitel wird neben den Beispielen mit den Wettermeldungen die Beispieldatei nsdemo.xml verwendet, die den unten gezeigten Inhalt besitzt.

Wannsee-Wahnsinns-Welle

98.35 104.15 92.10

Nightly Blues 22:00 23:30

68

Einführung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Jonny Setup

MO MI FR SA

Office Charts 10:00 11:00 Gabi Parser

MO DI MI DO



Radio Benchmark - die schnelle Welle

89.90 96.50

Rush Hour 15:30 17:00 Steve Q. Log

MO DI MI DO FR



Listing 6.3: Die Namensraum-Beispieldatei

Namespaces bei XmlReader und XmlWriter Beim Lesen von XML mit Hilfe des XmlReaders oder einer abgeleiteten Klassen können Sie auf verschiedene Eigenschaften zurückgreifen, um Informationen über die Namensräume der einzelnen Elemente zu erhalten. Das folgende Beispiel zeigt eine Methodendefinition, mit der die Datei nsdemo.xml gelesen wird. Der Code listet die Informationen zur Schachtelungstiefe, dem Elementnamen, einem evtl. vorhandenen Präfix und dem dazugehörigen Namensraum-URI auf. public static void DemoElementList() { XmlTextReader rdr = new XmlTextReader(@"c:\tmp\nsdemo.xml"); Console.WriteLine("{0}\t{1,-16}\t{2}\t{3}",

69

.NET

6 XML-Namensräume ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

"Tiefe", "Tag", "Präfix", "Namespace"); while(rdr.Read()) { if(rdr.NodeType == XmlNodeType.Element) Console.WriteLine("{0}\t{1,-16}\t{2}\t{3}", rdr.Depth, rdr.LocalName, rdr.Prefix, rdr.NamespaceURI); } rdr.Close(); } Listing 6.4: Auslesen von Namensrauminformationen bei XmlReader

Hier die Ausgabe des Programms:

Abbildung 6.1: Die Ausgabe von Listing 6.4

XmlWriter und Namespaces

Beim Schreiben von Elementen mit einer Instanz von XmlTextWriter oder einer von XmlWriter abgeleiteten Klasse können Sie für Elemente und Attribute ebenfalls eine Namensraumdefinition und ein Präfix angeben. Wird die Methode WriteStartElement in der Überladung mit einem lokalen Namen und dem Namensraum (also ohne Präfix) aufgerufen, hängt das Ergebnis davon ab, ob dieser Namensraum bereits gültig und bekannt ist oder nicht. Falls ja, dann schreibt der XmlTextWriter automatisch das passende Präfix in die Ausgabedatei. Ist der Namensraum bisher unbekannt, wird für das Element ein Verweis auf diesen Namensraum ohne Präfix erstellt (also der Default-Namensraum für das Element auf den angegebene URI gesetzt). Die Methode im Beispiel unten zeigt das programmatische Erstellen einer XML-Datei mit der Angabe von Namensräumen.

70

Einführung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

// schreiben von xml mit namensäumen für elemente public static void DemoNamespaceWriter() { string filename = @"c:\tmp\ausgabe.xml"; XmlTextWriter xw = new XmlTextWriter(filename, null); xw.Formatting = Formatting.Indented; xw.Indentation = 4; xw.IndentChar = (char)32; xw.WriteStartDocument(); xw.WriteStartElement("root"); // schreiben eines elements mit namensraum xw.WriteStartElement("aw","book", "urn:bookdemo-uri"); // attribut mit namensraumdaten schreiben xw.WriteAttributeString("aw", "isbn", "urn:bookdemo-uri", "0-0000-00000-0"); // element fuer bekannten namespace xw.WriteStartElement("titel", "urn:bookdemo-uri"); xw.WriteString("Demo-Titel"); xw.WriteEndElement(); // element fuer unbekannten namespace xw.WriteStartElement("autor", "urn:book-authors-uri"); xw.WriteString("An O. Nymous"); xw.WriteEndElement(); xw.WriteEndElement(); xw.Close(); Console.WriteLine("Datei {0} geschrieben", filename); } Listing 6.5: XmlTextWriter und Namensräume

Hier das Ergebnis des Methodenaufrufs:

Demo-Titel

71

.NET

6 XML-Namensräume ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

An O. Nymous

Listing 6.6: Die Ausgabedatei mit Namensraumdeklarationen

XmlNamespaceManager Die Klasse XmlNamespaceManager dient der Verwaltung und der Auflösung von Namensrauminformationen und wird immer dann verwendet, wenn eine XML-Datei über deklarierte Namensräume verfügt. Alle anderen XML-relevanten Klassen besitzen Überladungen für die bisher vorgestellten Methoden, bei denen eine XmlNamespaceManagerInstanz als Parameter angegeben werden kann. Namenstabellen verwalten Präfix- und Elementnamen

Bei der Instantiierung eines XmlNamespaceManager wird eine Tabelle als Parameter angegeben, der sogenannte »name table«. Hierbei handelt es sich um eine Tabelle mit atomisierten Zeichenfolgen für die Verwaltung der Elementnamen in der XML-Datei. Atomisiert deshalb, da jeder Name in dieser Tabelle nur einmal gespeichert wird, auch wenn es mehr als ein Element mit diesem Namen gibt. Nachdem Strings in der .NET-Umgebung nicht mehr verändert werden können (dies ist nur über die StringBuilder-Klasse möglich), kann somit auch ein Vergleich auf Gleichheit über die Objektreferenz erfolgen, der wesentlich schneller als ein Zeichenkettenvergleich durchgeführt werden kann. Mehr zur Stringverarbeitung inklusive Beispielen und Performancevergleichen zwischen Stringallozierung und StringBuilder in .NET finden Sie in meinem Buch "Goto C#", ebenfalls bei Addison-Wesley. Das folgende Code-Fragment erzeugt eine Instanz von XmlNamespace Manager und fügt eine Definition für einen Namensraum hinzu. XmlDocument thedoc = new XmlDocument(); thedoc.Load(@"c:\tmp\nsdemo.xml"); XmlNamespaceManager nsmgr = new XmlNamespaceManager(thedoc.NameTable); nsmgr.AddNamespace("radio", "urn:radio-uri"); Listing 6.7: Erzeugen einer XmlNamespaceManager-Instanz

t

Hier wird für die Instantiierung die vorhandene Namenstabelle des XmlDocuments benutzt. Nach der Erzeugung trägt der NamespaceManager automatisch drei Namensräume ein: Namespace:http://www.w3.org/XML/1998/namespace, Prefix:xml Namespace:http://www.w3.org/2000/xmlns/, Prefix:xmlns

72

Einführung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Außerdem wird ein Leerstring (String.Empty) für ein leeres Präfix eingetragen, also der Standardnamespace sozusagen gelöscht. Elemente ohne Präfix sind damit keinem Namensraum zugeordnet. Mit Hilfe der Methode AddNamespace fügen Sie eine zusätzliche Namensraumdeklaration hinzu. Dabei geht der Namespace-Manager davon aus, dass die Gültigkeit des Präfixes und des Namensraum-URI bereits geprüft wurde. Es wird hier keine weitere Überprüfung durchgeführt.

AddNameSpace

Die Klasse XmlNamespaceManager unerstützt Auflistungen über eine Implementierung der IEnumerable-Schnittstelle. Sie können also mit einer foreach-Schleife die Liste der eingetragenen Präfixe durchlaufen: Console.WriteLine("Folgende Präfixe sind eingetragen:"); foreach(string s in nsmgr) Console.WriteLine("Präfix \"{0}\" ", s); Listing 6.8: Auflisten aller eingetragenen Namensräume

Für die einzelnen Aufgaben der Verwaltung von Namensräumen sind in der Klasse eine ganze Reihe von Methoden und Eigenschaften definiert, mit der z.B. eine Zuordnung von Namensraum-URI zu Präfix in einer Zeile erledigt ist. Aufgabe

Methode oder Eigenschaft

Den Namespace-URI finden, der als Standardnamespace definiert wurde.

DefaultNamespace-Eigenschaft

Den Namespace-URI finden, der für ein Präfix deklariert wurde und im Gültigkeitsbereich liegt.

LookupNamespace-Methode

Das Präfix finden, das für einen NamespaceURI deklariert wurde und im Gültigkeitsbereich liegt.

LookupPrefix-Methode

Dem XmlNamespaceManager weitere Namespaces hinzufügen.

AddNamespace-Methode

Namespaces aus dem XmlNamespaceManager entfernen.

RemoveNamespace-Methode

Einen Gültigkeitsbereich für einen Namespace festlegen.

PushScope-Methode, PopScope-Methode

Überprüfen, ob im aktuellen Gültigkeitsbereich ein Präfix definiert ist.

HasNamespace-Methode

Den Namen der NameTable finden, die gegenwärtig in der Lookup- und in der Add-Methode verwendet wird.

NameTable-Eigenschaft

t

Tabelle 6.1: Aufgaben im Bereich Namensräume und die passenden Methoden dazu

73

.NET

6 XML-Namensräume ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

XPath und Namensräume Für die Selektierung von Elementen mit Hilfe einer XPath-Abfrage muss bei Namensräumen ebenfalls ein Namespace-Manager angegeben werden, der mit den entsprechenden Namensraumdeklarationen versorgt wurde. Anschließend erfolgt die Abfrage wie bereits bekannt per XPath-Ausdruck. SetContext

Es besteht allerdings ein Unterschied: es kann kein String als XPathAusdruck verwendet werden, sondern der Ausdruck muss vorher in eine Instanz einer XPathExpression kompiliert werden, da diese Klasse über eine Methode SetContext verfügt, mit der XPath-Ausdruck und Namespace-Manager verbunden werden. Beim XPath-Ausdruck selbst, der der Methode XPathNavigator.Compile übergeben wird, müssen die entsprechenden Präfixe für den Namensraum angegeben werden. Das Beispiel zeigt eine Methode mit diesen Änderungen im Code, um auch bei XPath-Abfragen auf Namensräume zugreifen zu können. public static void DemoNamespaceXPath() { XPathDocument thedoc = new XPathDocument(@"c:\tmp\nsdemo.xml"); XPathNavigator xnav = thedoc.CreateNavigator(); try { XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); nsmgr.AddNamespace("radio", "urn:radio-uri"); nsmgr.AddNamespace("leute", "urn:leute-uri"); XPathExpression xpr = xnav.Compile("//leute:moderator/@email"); xpr.SetContext(nsmgr); XPathNodeIterator iter = xnav.Select(xpr); while(iter.MoveNext()) { Console.WriteLine(iter.Current.Value); } } catch(Exception e) { Console.WriteLine("Error: ", e.Message); } } Listing 6.9: XPath-Auswahl von Knoten mit Namensräumen

74

Einführung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Validierung und Namensräume Bei der Validierung von XML-Daten, die Deklarationen für einen Namensraum enthalten, ändert sich kaum etwas, nur dass die Elemente entsprechend qualifiziert angegeben weren. Ein Sonderfall ist eine XML-Datei mit Elementen ohne Präfix (unqualifiziert), die über einen Standardnamensraum verfügen. Die Definition des Namensraums ist hier nochmals für die Beispieldatei meteo.xml aufgeführt:

Listing 6.10: Definition des Default-Namensraums (ohne Präfix)

Sie sehen die Definition eines Standardnamensraums für Elemente ohne Präfix in der vorletzten Zeile und in der letzten Zeile der Verweis, wo für diesen Namensraum das entsprechende Schema zu finden ist. Im Schema selbst erfolgt zuerst die Definition für den xsd-Namensraum, den Schemadefinitionsnamensraum. Anschließend wird das Namensraum-Präfix wx mit dem gleichen URI wie in der XML-Datei deklariert und danach der targetNamespace für das Schema ebenfalls auf diesen URI gesetzt, so dass das Dokument gegen dieses Schema geprüft werden kann. Bei beiden Angaben für elementFormDefault und attributeFormDefault entsprechen der Standardeinstellung und sind hier nur der Vollständigkeit halber aufgeführt. Deren Inhalt legt fest, dass Angaben unqualifiziert, also ohne Angabe des Präfix in der Datei erfolgen.

elementForm Default und attributeForm Default

Listing 6.11: Das -Element mit Namensräumen

Für die Validierung besteht der einzige Unterschied zur Validierung mit einem XSD-Schema darin, dass bei der Methode XmlSchemaCollection.Add nun die Angabe des Präfix zu einem Namensraum-URI erfolgt (ohne einen Namensraum war dies jeweils ein leerer String). Der Rest entspricht dem normalen Ablauf. Hier nochmal die komplette Klasse, damit Sie nicht zurückblättern müssen:

XmlSchema Collection

75

.NET

6 XML-Namensräume ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

class XSDSchemaDemo { private bool bValidated = true;

// annahme

// die ereignisbehandlungsroutine public void ValidationSink(object sender, ValidationEventArgs args) { // nur fehler ausgeben, warnungen ignorieren if(args.Severity == XmlSeverityType.Error) { bValidated = false; Console.WriteLine("Fehler: {0}", args.Message); } } // prüfen mit dem angeforderten schematyp public bool Validate(string url, ValidationType vt) { // schema collection mit schema fuellen XmlSchemaCollection xsc = new XmlSchemaCollection(); try { xsc.Add("urn:meteo-demo", @"c:\tmp\meteo.xsd"); //XSD schema } catch(Exception e) { Console.WriteLine("Exception thrown! {0}", e.Message); } // textreader erzeugen XmlTextReader txtrdr = new XmlTextReader(url); // den validatingreader erzeugen XmlValidatingReader vrdr = new XmlValidatingReader(txtrdr); vrdr.ValidationType = vt; // schemas zum validatingreader hinzufügen vrdr.Schemas.Add(xsc); // eintragen eventhandler vrdr.ValidationEventHandler += new ValidationEventHandler(this.ValidationSink); // jetzt keine exception mehr bei fehler

76

Zusammenfassung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

while(vrdr.Read()) { } Console.WriteLine(vrdr.Schemas.Count); // ergebnis zurückliefern return bValidated; } } Listing 6.12: Überprüfung mit XSD-Schema und Namensräumen

6.2 Zusammenfassung Namensräume sind für XML mittlerweile unverzichtbar, um die Vielfalt der gleichlautendenden Elementnamen auseinander halten zu können. Mit Hilfe eines URI sind sie leicht eindeutig zu spezifizieren. Über ein Präfix, der durch einen Doppelpunkt vom lokalen Namensteil des Elements oder Attributes getrennt wird, ist die Verwendung kein Problem. Einsteiger sollten bei der manuellen Erstellung eines Schemas auf die richtige Deklaration der Namensraum-URIs achten und sich nach Möglichkeit anfangs der Hilfe einer XML-Entwicklungsumgebung bedienen. Nach Möglichkeit sollten Sie auf die Deklaration eines Default-Namensraums (ohne Präfix) verzichten, wenn Sie mehr als einen Namensraum verwenden, da die Lesbarkeit und die Zuordnung der Elemente durch das Präfix erleichtert wird.

77

.NET

Essentials

7

XSL-Transformationen

7.1 Grundlagen XSLT ist wohl für viele Entwickler der komplizierteste Teil der XMLTechnologien und wird daher noch nicht überall in dem Maß eingesetzt, das der Bedeutung von XSL-Transformation gerecht würde. Eine komplette Einführung in die Thematik würde an dieser Stelle zu weit führen, aus Platzgründen werde ich mich auf die wichtigsten Punkte beschränken, so dass Sie schnell zu Ergebnissen kommen. Details und Weiterentwicklungen (wie die XSL-Formatting Objects) finden Sie bei den diversen Titeln zum Thema XSLT. Wie der Name bereits ausdrückt, handelt es sich hier um eine »Stylesheet-Language«, d.h. um das Anwenden von Formatierungsregeln auf alle in einem Stylesheet definierten Elemente des geparsten XML-Baumes. Das Zusammenwirken der einzelnen Elemente zeigt die Abbildung eines Transformationsvorgangs.

Statt XSLT wird oft auch XSL als Abkürzung verwendet

Abbildung 7.1: Ablauf einer XSL-Transformation

Eine Eingabestruktur in XML wird mit Hilfe eines Stylesheets, das die Formatierungsregeln enthält, zu einer Ausgabestruktur umgewandelt. Das Ergebnis dieser Transformation muss übrigens nicht unbedingt wieder eine XML-Datei sein, sondern kann durchaus HTML oder sogar Flatfile sein. Nur nebenbei: Sie benötigen nicht unbedingt .NET für die Nutzung von XSLT, die XML Core Services von Microsoft (der MSXML) sind mittlerweile in der Version 4 erhältlich und bieten auch in der alten COM-Welt die Unterstützung der W3C-Empfehlungen.

79

.NET

7 XSL-Transformationen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Aufbau eines Stylesheets Ein XSL-Stylesheet (oder XSLT-Transformationsdatei, wie die deutsche Version des .NET-Frameworks diese Dateien nennt) ist selbst ebenfalls eine XML-Datei. Die für XSLT nötigen Elemente werden im XSLNamensraum definiert, der durch den URI http://www.w3.org/1999/XSL/ Transform identifiziert wird und (per Konvention) das Präfix xsl trägt. Auch wenn theoretisch ein anderes Präfix ebenso gut möglich wäre, sollten Sie sich doch an xsl halten. Wer weiß schon, in wie vielen Anwendungen dieses Präfix fest eincodiert wurde … XSL-Templates

Innerhalb des Stylesheet-Rumpfes werden so genannte Schablonen (templates) definiert, die als Wert für das Attribut match jeweils einen XPath-Ausdruck enthalten. Matcht dieser (wird ein übereinstimmendes Element gefunden), so wird dieses Template abgearbeitet und die darin enthaltenen Elemente in den Ausgabestrom geschrieben.





The weather in

:



Listing 7.1: Eine sehr einfache XSLT-Datei

Das obige Stylesheet enthält ein Template mit dem Wurzelelement als Übereinstimmungsausdruck und eines, das für alle -Elemente als Kontextknoten oder dessen Nachfahren eine Übereinstimmung liefert. Für das Wurzelelement erfolgt nur die Ausgabe eines Absatz-Tags in HTML, gefolgt von einer rekursiven Anwendung aller anderen definierten Templates im Stylesheet (), danach wieder ein schließendes Tag für einen Absatz.

80

Stylesheets laden ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Die Schablone für alle -Elemente zeigt die Ausgabe von Textinformation, die unverändert in den Ausgabestrom geschrieben werden sollen (per ) und das Einsetzen von Elementinhalten über die Anweisung , die ebenfalls einen XPath-Ausdruck verwendet. Beispiele für komplexere Stylesheets finden Sie noch weiter unten in diesem Kapitel oder in einem der vielen Einführungen in das Thema XSLT.

7.2 Stylesheets laden Zum Laden von Tranformations-Stylesheets benutzen Sie die Methode Load der Klasse XslTranform, die mehrfach überladen ist. So kann das Stylesheet nicht nur aus einer Datei, sondern auch von einem bereits geparsten XPathDocument oder einem XmlDocument oder einem XmlReader stammen. Damit können auch programmatisch erzeugte Stylesheets leicht geladen und für die Transformation verwendet werden. Das folgende Beispiel lädt ein Stylesheet von einem URL:

XslTransform, Methode Load

XslTransform xslt = new XslTransform(); //Load the stylesheet. xslt.Load("http://localhost/demo/xsl/wetter1.xsl"); Listing 7.2: Laden eines bestehenden Stylesheets

Laden über einen XmlUrlResolver Wird ein Stylesheet (oder eine andere externe Ressource für XML wie z.B. eine DTD, ein Schema u.a.) über einen URL geladen, spielt die Berechtigung für diesen Zugriff eine Rolle. Besteht für das angegebene Vezeichnis auf dem IIS ein anonymer Zugriff, reicht die Angabe des URL aus. Für alle anderen Fälle muss ein sogenannter Resolver angegeben werden, der sich dann um die Weiterreichung der Benutzerinformationen kümmert. Die Klasse NetworkCredentials findet sich übrigens im Namensraum System.Net, den Sie importieren müssen. Das Listing zeigt das Laden eines Stylesheets mit Hilfe eines Resolvers und der Angabe von Benutzername und Kennwort.

Authentifizierung über Network Credentials aus System.Net

static void SampleResolverTransform() { XslTransform xslt = new XslTransform(); // neue resolver-instanz erzeugen XmlUrlResolver resolver = new XmlUrlResolver(); // benutzderdaten festlegen NetworkCredential nc =

81

.NET

7 XSL-Transformationen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

new NetworkCredential("usr","pwd","domain"); resolver.Credentials = nc; // stylesheet laden. xslt.Load("http://kiste/xsl/wetter1.xsl", resolver); // hier dann transformation durchführen } Listing 7.3: Laden von Ressourcen über einen Resolver

Diese Art des Zugriffs benötigen Sie insbesondere dann, wenn innerhalb des Stylesheets (oder des XML-Dokumentes) auf eine externe Datei verwiesen wird, da dann der Resolver die einzige Möglichkeit darstellt, diese Verweise unter Sicherheitsaspekten aufzulösen. Der XmlUrlResolver ist die Standardklasse (abgeleitet von XmlResolver) für diesen Zweck, Sie können allerdings auch eigene, spezielle Resolver programmieren. Weitere Hinweise dazu finden Sie in der Online-Hilfe von Visual Studio .NET.

7.3 Transformation durchführen Methode Transform

Die eigentliche Hauptarbeit führt die Methode Transform der Klasse XslTransform durch, durch deren Aufruf das Stylesheet und die XML-Eingangsdaten verbunden werden. Auch diese Methode ist wieder mehrfach überladen, so dass für jeden Zweck die passende Liste an Parametern zur Verfügung steht. So wird als erster Parameter die XMLStruktur übergeben, wobei Sie die Wahl zwischen einer Klasse haben, die die Schnittstelle XPathNavigable implementiert (XmlNode, XmlDocument oder XPathDocument) oder einer Instanz eines XPathNavigators. Der zweite Parameter ist eine Referenz auf eine Liste von Parameterersetzungen für das Stylesheet (siehe weiter unten) und der dritte Parameter gibt das Ziel der Transformation an, wobei hier ein Stream-Nachfolger, ein TextWriter oder ein XmlWriter benutzt werden können. Das folgende Listing ist der komplette Code für die Transformation einer XML-Datei mit Hilfe eines XSL-Stylesheets. Das Ergebnis der Transformation wird dann in die Standardausgabe geschrieben. Nach dem Listing folgt das Listing des verwendeten Stylesheets und in der Abbildung die Ausgabe des Programms. // XSL-Transformationen in .NET using using using using

82

System; System.XML; System.XML.XPath; System.XML.XSL;

Transformation durchführen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

namespace XSLDemo { class XSLDemos { static void SampleSimpleTransform() { XslTransform xslt = new XslTransform(); // stylesheet laden xslt.Load(@"h:\wetter1.xsl"); // XPathDocument erzeugen und die // xml-daten laden XPathDocument thedoc = new XPathDocument(@"h:\miniwetter.xml"); // XmlTextWriter fuer die ausgabe // erzeugen XmlTextWriter writer = new XmlTextWriter(Console.Out); // transformation durchführen // ausgabe geht nach Console.Out, wie // durch XmlTextWriter-Erzeugung festgelegt xslt.Transform(thedoc,null,writer); } static void Main(string[] args) { SampleSimpleTransform(); Console.WriteLine(); Console.Write("Press a key ...."); Console.ReadLine(); } } } Listing 7.4: Transformation eines XML-Dokumentes per XSL

Hier das verwendete Stylesheet:





83

.NET

7 XSL-Transformationen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials



The weather in

:



Listing 7.5: Demo-Stylesheet wetter1.xsl

Im Konsolenfenster wird die folgende Ausgabe erzeugt:

Abbildung 7.2: Die Ausgabe im Konsolenfenster

XML-Strukturen mit XSLT umformen Xml-Editoren als sinnvolles Werkzeug

Mit Hilfe von XSL-Stylesheet und der Klasse XslTransform lassen sich nicht nur XML-Daten in ein anderes Format überführen. Eines der Haupteinsatzgebiete ist die Umformung einer XML-Struktur in eine andere Struktur, um Daten für verschiedenene Anwendungen anzupassen. So ist die Beispieldatei meteo.xml mit Hilfe einer XSL-Transformation aus der Datei wetter.xml entstanden. Produkte wie z.B. der BiztalkServer haben diese Funktionaliät als Grundlage. Visual Studio .NET bietet zwar eine Möglichkeit zur Erstellung von XSLT-Dateien, aber für diese Stylesheets existiert in der aktuellen Version keine Unterstützung durch Autovervollständigung, IntelliSense und all die anderen guten Dinge aus dem Code-Editor. Für die Erstellung von komplexeren Stylesheets sollten Sie sich daher eine XML-Entwicklungsumgebung wie XMLSpy oder Stylus Studio ansehen. Das nachfolgende Stylesheet erzeugte aus der Datei wetter.xml die Dateo meteo.xml für die weiteren Beispiele. Mit Hilfe der XSL-Funktionen lassen sich dabei auch Teile eines Elementinhaltes verwenden, wie die Funktion substring-before für das Abschneiden des Prozentzeichens bei der Luftfeuchtigkeit zeigt.

84

Transformation durchführen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials















percent





knots





hpa



85

.NET

7 XSL-Transformationen ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials



Listing 7.6: Komplexeres Beispiel für eine XSL-Transformation

7.4 Transformationen verketten und optimieren Transformationen verketten Vermeiden Sie unnötiges Parsing

Nicht immer reicht eine einzelne Transformation aus. So sind durchaus Szenarien denkbar, bei denen zuerst eine XML-Datei mit Hilfe eines Stylesheets in ein Stylesheet transformiert wird und dann dieses Stylesheet benutzt wird, um eine XML-Datendatei zu transformieren. Oder eine Datei wird nacheinander von drei verschiedenen Stylesheets umgeformt. Dazu sollte nach Möglichkeit vermieden werden, einen neuen Parse-Vorgang zu starten. Dies ist beispielsweise dann der Fall, wenn das Ergebnis der ersten Transformation (das Zwischenergebnis) in einen Stream (z.B. eine Instanz von MemoryStream) geschrieben wird. Sinnvoller ist die Nutzung eines XmlReader, aus dem sich dann eine neue Instanz eines XPathDocument erzeugen lässt, die gleich die durch den Reader geparsen Inhalt übernimmt. Je nach Szenario und Dateistruktur lassen sich dadurch deutliche Performancesteigerungen erzielen. Hier ein Beispiel für eine zweifache Transformation. Einsatzmöglichkeiten in der Praxis bieten sich beispielsweise für Redaktionssysteme, die XML-Content von XML nach XHTML rendern und danach in einem zweiten Durchlauf das »nackte« XHTML durch Formatierungen wie Inline-Styles oder andere Markup-Attribute ergänzen. // mehrfache transformationen mit // koppelung über einen XmlReader static void SampleChainedTranform() { XslTransform xslt1 = new XslTransform(); XslTransform xslt2 = new XslTransform(); // stylesheets laden xslt1.Load(@"h:\wetter1.xsl"); xslt2.Load(@"h:\wetter2.xsl"); // XPathDocument erzeugen und datei laden XPathDocument thedoc = new XPathDocument(@"h:\miniwetter.xml"); // XmlTextWriter für den output erzeugen

86

Zusammenfassung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

XmlTextWriter writer = new XmlTextWriter(Console.Out); // Zwischenergebnis in einen XmlReader stecken XmlReader temprdr = xslt1.Transform(thedoc, null); // XPathDocument aus diesem Reader erzeugen XPathDocument doc2 = new XPathDocument(temprdr); // nochmal transformieren und an die konsole xslt2.Transform(doc2,null,writer); } Listing 7.7: Mehrfache Transformationen per XSL

XPathNavigable-Implementierungen nutzen Aus Gründen der Performance sollte für die Transformation nach Möglichkeit die XML-Struktur zuerst in eine XPathDocument-Instanz geladen werden, da diese die Schnittstelle XPathNavigable implementiert und für die Verarbeitung von Stylesheets optimiert wurde. Das obige Listing zeigt diesen Ansatz. Sie können natürlich auch einfach der URL für die Eingabe- und die Ausgabedatei angeben, wobei für kleinere Dateien der Performanceunterschied kaum ins Gewicht fällt. Ganz anders sieht die Sache dagegen aus, wenn Sie eine 15 Mbyte große XML-Struktur mit zwei Stylesheet-Transformationen in eine andere XML- oder Editfact-Datei überführen.

Mehrfaches Laden von Stylesheets vermeiden Vermeiden Sie nach Möglichkeit das unnötige Laden von Stylesheets, da hierbei jedesmal von neuem das Dokument geparst und eventuelle externe Referenzen aufgelöst werden müssen. Werden mehrere Dokumente mit dem gleichen Stylesheet behandelt, sollte das Laden per Load außerhalb der Schleife erfolgen und nicht jedesmal neu in der Schleife.

7.5 Zusammenfassung Mit nur wenigen Methoden (hautpsächlich Load und Transform) bietet die XSLT-Unterstützung von .NET eine mächtige Möglichkeit, beliebige XML-Strukturen programmgesteuert zu transformieren und sogar die Stylesheets selbst als XML-Dokumente zuerst programmatisch zu erzeugen. Die sich bietenden Möglichkeiten werden nur durch die Fantasie des Entwicklers begrenzt. Entwerfen Sie doch einfach eine XMLNotation für Klassen oder benutzen Sie eine der verfügbaren und schreiben sich dann dazu ein XSL-Stylesheet, das je nach Auswahl entweder C#- oder Visual Basic .NET-Code erzeugt!

87

.NET

Essentials

8

Validieren von XML

8.1 Warum ein Schema? In der Regel werden XML-Strukturen von Anwendungen weiterverarbeitet. Daher besteht ein Interesse daran, dass eine zur Bearbeitung vorliegende Datei in ihrem Aufbau vorhersagbar ist bzw. sich an einen vereinbarten Standard hält. Ist dies nicht der Falls, kann die Datei unter Umständen nicht verarbeitet werden (wenn z.B. bei einer Bestelldatei in XML die Angabe der Teilenummer fehlt). Daher wurden bereits zu Beginn der XML-Entwicklung Möglichkeiten von SGML übernommen, die Struktur der Datei zu beschreiben und so einen Test auf Einhaltung dieser Struktur zu ermöglichen. Diese erste Variante eines Schemas war die Document Type Definition (DTD). Aus verschiedenen Gründen (die in den folgenden Abschnitten noch erläutert werden) ist eine DTD aber nicht das ideale Mittel zur Beschreibung der Grammatik einer XML-Datei. Standard ist derzeit die vom World Wide Web Consortium (W3C) als Empfehlung verabschiedete XMLSchema Definition (XSD), die das zweite hier erläuterte Mittel zur Defintion einer XML-Grammatik darstellt.

DTDs sind noch relativ häufig

Aufbau einer DTD Aus Gründen des zur Verfügung stehenden Platzes und da DTD meist nur noch in Form von Portierungsquellen für XSD-Schemata auftauchen, sind die nächsten Abschnitte nur eine kurze Einführung. Weitere Details zum Erstellen von DTD-Schemata finden Sie in den allgemeinen XML-Büchern zum Thema bzw. dem Internet. Anhand einer Document Type Definition lassen sich aber schön der Weg von der Struktur zum Dokument und auch die Nachteile eines Textansatzes zeigen, der schließlich zu den XSD-Schemata führte. Eine DTD beschreibt, welche Elemente und Attribute in welcher Reihenfolge auftreten, ob ein Attribut erforderlich ist und diverse weitere Details der XML-Struktur. Diese Beschreibung ist eine Textdatei (kein XML, was einen der Hauptnachteile der DTDs darstellt) und besitzt für die Beispieldatei Meteo.xml folgenden Aufbau:

sky (#PCDATA)*> temp (#PCDATA)*> temp units (Celsius|Fahrenheit|Kelvin) #REQUIRED> humidity (#PCDATA)*> humidity units CDATA #REQUIRED> wind (#PCDATA)*>

89

.NET

8 Validieren von XML

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

airpressure (#PCDATA)*> airpressure CDATA #REQUIRED CDATA #REQUIRED> station ((sky|clouds),temp,humidity,wind,airpressure)> station id CDATA #REQUIRED> weatherdata (station)+>

Listing 8.1: Eine Document Type Definition

Definition von Elementen Wie sich relativ leicht erkennen lässt, werden mögliche Elemente mit dem Ausruck definiert, der als Parameter den Namen des Elements und in runden Klammern die Liste der Unterelemente erhält. Werden als Elementinhalt beliebige Zeichenketten an Stelle von weiteren Unterelementen akzeptiert, wird dies durch #PCDATA angegeben. Diese Abkürzung steht für »parsed character data« und erlaubt beliebige Zeichenketten. Ein Beispiel für Unterlemente finden Sie bei der Definition , hier werden die notwendigen Unterlemente aufgelistet. Da in der Datei entweder das Tag oder die Bewölkung mit vorkommen darf, wird diese Auswahl in der DTD über den choice operator "|" festgelegt. Dieser arbeitet wie ein logisches Oder und kann in einer längeren Liste auch mehrfach wiederholt werden, wie Sie bei den Attributdefinitionen noch sehen werden. Für das Auftreten von Elementen kann angegeben werden, ob und wie oft ein Element als Unterelement auftreten darf. In der Beispieldatei muss innerhalb des -Elementes mindestens ein Element vorhanden sein, was in der DTD durch das nachgestellte "+" definiert wird. Wer bereits mit regulären Ausdrücken gearbeitet hat, kennt die möglichen Modifikatoren bereits:

t

Zeichen

Bedeutung

?

Optionales Element, muss nicht vorhanden sein

*

Dieses Element kann 0-mal oder öfter auftauchen

+

Dieses Element muss mindestens einmal vorhanden sein

|

Choice operator, eines der Elemente aus der Liste

Tabelle 8.1: Die möglichen Modifikatoren für Elemente und Attribute

90

Warum ein Schema?

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Definition von Attributen Für die Definition von Attributen dient das -Statement, mit dem für ein als Parameter angegebenes Element die Liste der möglichen Attribute definiert wird. Nach dem Namen des Elements folgt der Attributname und anschließend der mögliche Inhalt, entweder CDATA für Zeichenfolgendaten oder die direkte Angabe eines oder einer Reihe möglicher Werte mit dem choice-Modifikator. Für Attribute existieren vier mögliche Instanztypen: Fixed

Das Attribut muss dem angegebenen Wert entsprechen und muss immer explizit angegeben werden

Implied

Das Attribut ist optional, es existiert kein Defaultwert

Optional

Das Attribut ist optional, wird kein Attribut angegeben, setzt der Parser den Default-Wert ein

Required

Es muss immer explizit ein Attributwert angegeben werden

t

Tabelle 8.2: Die möglichen Belegungstypen für Attributinhalte

Interne und externe DTDs Die obige DTD ist eine interne DTD, da dieser Text in der XML-Datei selbst enthalten ist:



2 Laphroaig Kildalton Active

3 Lagavulin Kildalton Active

Im Gegensatz dazu enthält die mit der Methode WriteXml geschriebene Datei nur die Elemente, die für den Aufbau des DataSets relevant sind. Auch beim Lesen per ReadXML wird übrigens nur der Dateninhalt gelesen und ebenso Nichtdaten-Elemente und Leerraum entfernt.

124

Zusammenfassung ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

2 Laphroaig Kildalton Active

3 Lagavulin Kildalton Active

9.4 Zusammenfassung Die zentrale Rolle von XML im .NET-Framework kommt gerade bei ADO.NET deutlich zum Tragen. Als internes Format für die Speicherung von nicht verbundenen DataSets und den Transport von Daten zwischen Maschinen und Prozessen ist XML das zentrale Format. Somit können Sie jede beliebige gültige XML-Datei als Tabelle laden und in Tabellenform bearbeiten oder Datentabellen als hierarchische XML-Daten über eine Firewall hinweg per http versenden. Mit Hilfe der XmlDataDocument-Klasse sind Sie in der Lage, gleichzeitig relational mit ADO.NET-Methoden und über das XML-Datenmodell auf ein und denselben Datenbestand zuzugreifen.

125

.NET

Essentials

10 XML-Dokumentation C# ist derzeit die einzige Programmiersprache von Microsoft, die eine Möglichkeit zur Erstellung von XML-Kommentaren bietet. Ähnlich wie javadoc bei Java lassen sich somit bei Bedarf automatisch XML-Dateien erzeugen, die anschließend mit Hilfe eines XSLT-Stylesheets in ein beliebiges Format (z.B. HTML) transformiert werden können.

C# als Vorreiter

Zur Erstellung der XML-Datei mit den Dokumentationstags dient die Option /doc: des Compilers bzw. die Menüoption ERSTELLEN VON KOMMENTARWEBSEITEN im Menü EXTRAS, falls Sie Visual Studio .NET verwenden. In den Projekteinstellungen können Sie ebenfalls einen Namen eintragen, unter dem diese XML-Datei automatisch erstellt werden soll.

10.1 XML-Kommentare Erstellen von Kommentaren Ein XML-Kommentar wird durch drei Schrägstriche (also den C#-Kommentar um einen weiteren Schrägstrich ergänzen) eingeleitet. Visual Studio .NET hilft auch hier wieder mit einem Automatismus: falls Sie den Kommantar beispielsweise über einer Klasse oder Methode beginnen, werden automatisch Parameter ausgelesen und ein -Tag erzeugt. Sie können solche XML-Kommentare vor einer Klasse oder ein Mitglied der Klasse (Exception, Delegate, Methode, Eigenschaft) erstellen. XMLKommentare im Code eines Klassenmitglieds oder vor einem Namensraum werden nicht akzeptiert und der Compiler gibt eine Warnung aus:

/// startet einen XML-

Kommentar

t

C:\tmp>csc /doc:Class1.xml Class1.cs Microsoft (R) C#-Compilerversion 7.00.9466 für Microsoft (R) .NET Framework Version 1.0.3705 Copyright (C) Microsoft Corporation 2001. Alle Rechte vorbehalten. Class1.cs(61,4): warning CS1587: Der XML-Kommentar ist auf keinem gültigen Sprachelement abgelegt Listing 10.1: XML-Kommentare dürfen nicht überall stehen.

Damit fehlt leider eine Möglichkeit, eigene XML-Tags an geeigneter Stelle im laufenden Code in die Datei einzubringen. Vielleicht integriert Microsoft diese Funktionalität ja in die nächste Version des Frameworks.

127

.NET

10 XML-Dokumentation ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Zur Verdeutlichung der Einsatzmöglichkeiten zeigt das nachfolgende Listing einen Ausschnitt aus einer Klasse mit XML-Kommentaren: /// /// Diese Methode führt die eigentliche /// Validierung durch. Für jeden Namensraum, /// der benutzt wird, muss mit der Methode /// xsc.Add("urn:meteo-demo", "meteo.xsd") /// das entsprechende Schema geladen und der /// Präfix des Namensraums /// definiert werden. /// /// URL bzw. Dateiname der zu prüfenden Datei /// /// Validierungstyp, entweder Auto, DTD, None, XDR /// oder Schema /// true = Dokument wurde validiert, /// false = Fehler bei Validierung public bool Validate(string url, ValidationType vt) Listing 10.2: XML-Kommentare in Listings

Die Kommentarelemente Folgende Kommentar-Tags sind im Code verwendbar

Kennzeichnet als Code zu formatierenden Text in einem Kommentar. Dieses Element ist für Code im Fließtext des Kommentars gedacht.

Kennzeichnet als Code zu formatierenden Text in einem Kommentar. Dieses Element ist für mehrzeiligen Code und Listingsabschnitte im Kommentar gedacht.

Definiert ein Beispiel in einem Kommentar und wird meist in Kombination mit dem -Element verwendet.

Kommentiert die Exceptions, die eine Klasse auslösen kann und enthält ein Attribut cref mit dem Inhalt einer im Kontext verfügbaren Exception-Klasse. Beispiel: Thrown when...

(Syntax wird vom Compiler überprüft) Beispiel zu include weiter unten

128

Erlaubt den Verweis auf eine andere Datei und die Einbindung von mit einem XPath-Ausdruck übereinstimmenden Elementen aus dieser Datei. Im nächsten Abschnitt finden Sie ein Beispiel zu diesem Tag (Syntax wird vom Compiler überprüft).

XML-Kommentare ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

Dient der Erstellung von Listen oder Definitionslisten im Kommentar.

Definiert einen Absatz. Damit lassen sich längere Texte innerhalb des - , - oder -Elements besser strukturieren.

Bescheibt einen Parameter, der im Attribut name angegeben wird und in einfachen Anführungszeichen angegeben werden muss. Der Text des Elements ist die Beschreibung des Parameters (Syntax wird vom Compiler überprüft).

Verweist auf einen Parameter im Code. Der Name des Parameters wird als Werts für das Attribut name in doppelten Anführungszeichen angegeben (Syntax wird vom Compiler überprüft).

Dient der Dokumentation des Zugriffs auf ein Mitglied der Klasse und enthält ein Attribut cref, dessen Wert in doppelten Anführungszeichen ein im Kontext ereichbares PermissionSet darstellt (Syntax wird vom Compiler überprüft).

Dient der Beschreibung einer Klasse oder eines Mitglieds der Klasse und kann zusätzlichen, längeren Text enthalten, während eher für eine kurze zusammenfassende Beschreibung ohne Beispiele oder Code gedacht ist.

Beschreibt den Rückgabewert einer Methode.

Definiert einen Link auf ein Mitglied der Klasse, das als Wert für das Attribut cref in doppelten Anführungszeichen angegeben wird (Syntax wird vom Compiler überprüft).

Definiert einen Link auf ein Mitglied der Klasse, das als Wert für das Attribut cref in doppelten Anführungszeichen angegeben wird. dient dazu, diesen Link unter dem Abschnitt "Siehe auch" auszugeben. Wenn Sie die XML-Datei selbst verarbeiten, können Sie und nach eigenen Wünschen aufteilen (Syntax wird vom Compiler überprüft).

Übersichtsartige Beschreibung eines Klassenmitglieds, die von Visual Studio .NET auch für die Anzeige von Quicktipps bei IntelliSense verwendet wird. Für eine längere Beschreibung des Datentyps selbst sollten Sie zusätzlich verwenden.

129

.NET

10 XML-Dokumentation ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Dokumentiert eine Eigenschaft der Klasse. Geben Sie hier Informationen über den Typ und die Verwendung der Eigenschaft an. Darüber hinaus können Sie bei Bedarf eigene XML-Elemente benutzen. Solange es sich bei den verwendeten Konstrukten um wohlgeformtes XML handelt, sind Sie völlig frei in der Verwendung eigener Elemente wie z.B. , oder anderen Elementen, die natürlich auch Attribute beinhalten dürfen.

Einbinden von externen Dateien Mit Hilfe des Elements ist es möglich, den XML-Kommentar von der Codedatei zu trennen und in der Klasse nur einen Verweis auf die Datei mit den Kommentaren anzugeben. Das Element besitzt zwei Attribute, deren Werte in einfachen Anführungszeichen eingeschlossen werden müssen. Das erste Attribut file enthält den Pfadnamen der XML-Datei mit den Kommentaren. Das Attribut path enthält einen XPath-Ausdruck, mit dem innerhalb der Datei die Elemente selektiert werden, die an dieser Stelle eingefügt werden sollen. Der Kommentar in der Klasse DemoClass.cs lautet beispielsweise wie folgt: class Test { /// public static void Main() { } /// public void DoIt() { } } Listing 10.3: Verweis auf Includedateien in XML-Kommentaren

Hier die dazugehörige Datei Democlass.inc mit den Kommentaren

The summary for this type.

130

XML-Kommentare ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials

The summary for this other type.

Listing 10.4: Eine reine Kommentardatei zur Einbindung per

Damit ist unter Umständen zwar wieder eine Trennung von Code und Dokumentation verbunden, was aus softwaretechnischer Sicht nicht optimal ist. Als Vorteil bietet sich hier aber die Möglichkeit, diesen Teil automatisiert zu generieren oder per XSLT aus einem XML-fähigen Case-Tool herzuleiten. Auch eine Weiterverarbeitung dieser Datei mit anderen Tools wie zur Erstellung von Hilfedateien (HTML Help Workshop) ist denkbar.

Externe Dateien besitzen Vor- und Nachteile

Generell liegt der Vorteil der XML-Kommentare weniger in der Verwendung als im Code vorhandener Dokumentation für den Endanwender der Applikation, sondern in einer Hilfestellung für die Benutzer von Bibliotheken, die mit C# erstellt werden. Hier ist ein Entwickler, der Klassenbibliotheken benutzen möchte, auf eine gute und aktuelle Dokumentation der Schnittstellen angewiesen. Durch die Integration dieser Informationen in die Quellcodedatei wird es dem Programmierer leicht gemacht, diese aktuell und mit der Funktionalität des Codes synchron zu halten. Auch wenn damit ein etwas höherer Aufwand bei der Erstellung des Codes verbunden ist, sollten Sie daran denken, dass Quellcode Dutzende Male öfter gelesen als geschrieben wird und jede Zeile, die das Verständnis beim Lesen erleichtert, effektiv Zeit einspart.

Kommentardatei erstellen Mit Hilfe der Option /doc: wird die XML-Datei mit den Informationen generiert. Alternativ könenn Sie diese Option auch in Visual Studio .NET festlegen, indem Sie in den Eigenschaften des Projektes den Ordner KONFIGURATIONSEIGENSCHAFTEN anklicken und dort in der Seite ERSTELLEN das Feld XML-DOKUMENTATIONSDATEI eintragen. Die generierte XML-Datei löst bei den Elementen, für die der Compiler die Syntax überprüft, die Referenzen auf die Mitglieder der Klasse oder zu anderen Typen auf und generiert dazu eine eindeutige Bezeichnung des Typs, die Sie in einer weiterverarbeitenden Anwendung benutzen könenn.

Datentypen erhalten eine ID

131

.NET

10 XML-Dokumentation ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

t

Diese ID ist wie folgt aufgebaut: Der erste Teil der ID-Zeichenfolge kennzeichnet die Art des zu identifizierenden Members durch ein einzelnes Zeichen, gefolgt von einem Doppelpunkt. Die folgenden Membertypen werden verwendet: Zeichen

Beschreibung

N

Namespace

T

Typ: Klasse, Schnittstelle, Struktur, Auflistung, Delegat

F

Feld

P

Eigenschaft, einschließlich Indexer

M

Methode, Konstruktoren, Operatoren usw.

E

Ereignis

!

Fehlerzeichenfolge; der Rest der Zeichenfolge stellt Informationen über den Fehler bereit. Vom C#-Compiler werden Fehlerinformationen für Links, die nicht aufgelöst werden können, erstellt.

Tabelle 10.1: Typkennzeichner für ID-Zeichenfolgen in Kommentaren

Beim zweiten Teil der Zeichenfolge handelt es sich um den voll gekennzeichneten Namen eines Elements, beginnend mit dem NamespaceStammverzeichnis. Der Name des Elements und die umschließenden Typen sowie der Namensraum sind durch Punkte getrennt. Wenn der Name des Elements selbst Punkte enthält, werden sie durch ein Nummernzeichen (#) ersetzt. Es wird vorausgesetzt, dass kein Element ein Nummernzeichen direkt im Namen aufweist. Der voll gekennzeichnete Name des String-Konstruktors würde beispielsweise "System.String.#ctor" lauten. Wenn es sich bei Eigenschaften und Methoden um Argumente der Methode handelt, folgt die in Klammern eingeschlossene Argumentliste. Wenn keine Argumente vorhanden sind, werden keine Klammern verwendet. Die Argumente werden durch Kommas voneinander getrennt. Die Codierung jedes Arguments erfolgt genauso wie die Codierung in einer .NET Framework-Signatur. Hier ein Beispiel für eine generierte Datei mit ID-Zeichenfolgen:

Class1



Die Demo-Klasse für die XSD-Validierung. Wird von Main() aufgerufen.

132

XML-Kommentare ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET

Essentials



privates Feld, gibt an, ob die validierung geglückt ist.



Ereignisbehandlungsroutine für die validierung der datei

das ereignisauslösende objekt

ValidationEventArgs, enthalten die parameter fuer das ereignis



Diese Methode führt die eigentliche Validierung durch. Für jeden Namensraum, der benutzt wird, muss mit der Methode xsc.Add("urn:meteo-demo", "meteo.xsd") das entsprechende Schema geladen und der Präfix des Namensraums definiert werden.

URL bzw. Dateiname der zu prüfenden Datei Validierungstyp, entweder Auto, DTD, None, XDR oder Schema true = Dokument wurde validiert, false = Fehler bei Validierung

Testklasse für die Validierung mit XSD-Schema



Main-Methode für die Testklasse, hier geht's los.

Der Code

133

.NET

10 XML-Dokumentation ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

Console.WriteLine(); Console.Write("Press a key ...."); Console.ReadLine();

dient nur dazu, das konsolenfenster solange offen zu halten, bis der benutzer mit ENTER das fenster schliesst.



Listing 10.5: Die erstelle XML-Dokumentationsdatei

Ohne Visual Studio müssen Sie die XSL-Stylesheets selbst entwerfen.

Diese Datei kann nun mit Hilfe eines XSL-Stylesheets und einer Instanz der XslTransform-Klasse in einer nur wenige Zeilen langen Konsolenanwendung in eine optisch ansprechendere Form gebracht oder in ein Updategram zur Speicherung in einer Datenbank konvertiert werden. Auch hier automatisiert Visual Studio .NET mit dem Menüpunkt ERSTELLEN VON KOMMENTARWEBSEITEN im Menü EXTRAS den größten Teil der Arbeit.

10.2 Zusammenfassung Obwohl noch einige Wünsche offen bleiben (wie eigene Tags oder die Einbeziehung von Kommentaren innerhalb des Codes eines Klassenmitglieds) ist die Möglichkeit zur Dokumentation des Codes mit Hilfe von XML ein guter Ansatz und erleichert das automatisierte Erstellen von Dokumentationen im XML-Format deutlich. Über XSL-Stylesheets ist zudem die Transformation der so gewonnen Daten in ein anderes Format sehr leicht möglich.

134

.NET Essentials

11 Ausblick Dieser Band sollte Ihnen als Entwickler einen kompakten Schnellstart in die produktive Arbeit ermöglichen, wie der Titel »Essentials« nahelegt. Für eine komplette und ausführliche Behandlung aller Aspekte von XML in .NET wäre wohl der zehnfache Umfang notwendig. Natürlich fehlt noch eine ganze Menge: das Thema »Web Services und SOAP« nutzt XML als integralen Bestandteil, ADO.NET konnte nur kurz gestreift werden und die XML-Konfigurationsdateien und Sicherheitseinstellungen der Applikationen wären ebenso einen eigenen Band in dieser Reihe wert. XPointer und XQuery sind dem Schwerpunkt .NET-XML zum Opfer gefallen. Auch bei ASP.NET ist nicht alles HTML, sondern auch hier ist XML ein wichtiger Bestandteil. Für ASP.NET Services und ADO.NET möchte ich an dieser Stelle an die entsprechenden Bände meiner Autorenkollegen und -kolleginnen in der .NET-Essentials-Reihe verweisen. Für die anderen Themen sind bereits weitere Ausgaben zu dieser Reihe in Planung. Da das Thema XML in all seinen Facetten in jedem Bereich von .NET erscheint, sei es nun die XML-Abfrage beim SQL-Server 2000 oder eine Sicherheitsrichtlinie für .NET-Applikationen, hat auch Microsoft die immense Bedeutung erkannt und bietet in der Online-Dokumentation zum .NET-Framework und auch in der MSDN-Bibiothek (http:// msdn.microsoft.com) eine ganze Reihe von Grundlagenartikeln und weiterführenden Whitepapers an. Während der Arbeit an einem solchen Buch entwickeln sich auch die Standards im XML-Bereich weiter. XPath 2.0, XML 2.0 und die XQueryLanguage, die XSL Formatting Objects und andere neue Entwicklungen sind teilweise noch nicht einmal als Empfehlung des W3C verabschiedet worden. Im Grafikbereich existieren mit VML und SVG zwei Standards, die miteinander um den Platz als Vektorgrafikstandard für das Web konkurrieren (wobei die persönliche Vorliebe des Autors SVG gilt). Das Thema XML-Schemas (XSD) wird von vielen Stellen als zu komplex und nicht die Vorgaben erfüllend (leichtverständlich, knapp) beurteilt und auch hier sind (beispielsweise mit Relax NG) alternative Entwicklungen im Gange. Dennoch wird sich der XSD-Standard nach Meinung des Autors durchsetzen, da mit Hilfe von »annotated schemas« und den Möglichkeiten von XSD, Vererbungen auszudrücken, eine größere Flexibilität als bei reinen Mustergrammatiken oder DTDs besteht. In den Folgebänden der .NET-Essentials werden diese und weitere Themenkomplexe aufgegriffen. Die Autoren freuen sich darauf, Sie mit weiteren Informationen aus der Praxis in kompakter Form zu versorgen.

135

.NET Essentials

Stichwortverzeichnis ! .NET-Namensräume 12 /doc 127

XmlDocument 48 XmlValidatingReader 94 Evaluate 63

A AddNamespace 73 ADO.NET 113 Grundlagen 113 Programmiermodell 113 Attribute, XmlAttributeCollection 43 Attributes, Auflistung 43

G GetElementFromRow 122 GetElementsByID 42 GetRowFromElement 122 Gültigkeit, XML-Dokument 11

C CDATA 10 CreateAttribute 46 CreateNavigator 61 D DataSet 113 erstellen 114 Tables-Auflistung 114, 120 typisiert 120 XML laden 117 XML schreiben 119 Deserialisierung 29 DOCTYPE 91 Document Object Model siehe DOM Dokumentationstags 127 Dokumentbaum 39 DOM 39 Inhalte ändern 44 DTD 89 Alternativen 96 Aufbau 89 intern, extern 91 validieren 92 E Elemente, DOM 40 Entitäten siehe Entities Entities 10 Ereignisbehandlung, XmlDocument 48 Ereignisse Serialisierung 35

I IEnumerable 73 InnerText 41 IXPathNavigable 61 K Knoten, aktueller 16 Knotenmengen, auswählen 62 Knotentypen 17 Kommentar, XML 10, 127 Kommentardatei 131 Kompilieren, XPath-Ausdrücke 64 Kontextknoten, XPath 55 L Leerraum, XML 9 Load, XmlDocument 39 LoadFromXml, komplexe Datentypen 33 LoadXml 40 N Namensräume 11 NodeChanged, XmlDocument 49 NodeInserted, XmlDocument 49 NodeType 40 noNamespaceSchemaLocation 109 O OuterXml 41 P Präfix, Namensräume 67

137

.NET

Stichwortverzeichnis ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Essentials

R ReadXML 124 ReadXml 118 RemoveAll 45, 52 RemoveAttribute 47 RemoveChild 45 S SaveToXml, komplexe Datentypen 33 SchemaLocation 109 SelectNodes, Funktionen 59 SelectSingleNode, Funktionen 59 Serialisierung 27 komplexe Datentypen 33 Steuerung der 32 XML 27 XML-Inhalte 29 SetContext 74 Streaming, XML lesen und schreiben 15 Stylesheet 79 System.Data.OleDb 113 System.Data.SqlClient 113 System.Xml 13 System.Xml.Schema 13 System.Xml.Serialization 13 System.Xml.XPath 13 System.Xml.Xsl 13 T targetNamespace 68 Transform 82 Transformationen, optimieren 86 Typisierte DataSets 120 U Unbekanntes XML, Serialisierung 35 UnknownAttribute, Serialisierung 35 UnknownElement, Serialisierung 35 UnknownNode, Serialisierung 35 URI 67 V Verarbeitungsanweisung, XML 10 W Wohlgeformtheit, XML-Dokument 11 WriteElementString 22 WriteEndDocument 22 WriteEndElement 22 WriteFullEndElement 23

138

WriteStartDocument 22 WriteStartElement 22 WriteStartelement, Namensräume 70 WriteXml 119, 123 WriteXmlSchema 115

X XDR 96 XML Baumstruktur 9 Dateien schreiben 21 Entity 10 Leerraum 9 Lesen von Dateien 15 und Daten 113 XmlAttributeCollection 43 XmlDataDocument 121 Load 122 Methoden 122 XmlDocument 39 XML-Dokumentation 127 XML-Dokumentationsdatei 131 XML-Dokumente 39 Streaming oder DOM 39 validieren 89 XmlElement, Attributbehandlung 47 XML-Kommentare 127 Datei erstellen 131 Dateien einbinden 130 Elemente 128 XML-Namensräume 12, 67 Grundlagen 67 URI 67 Validierung 75 XmlNamespaceManager 72 Namenstabellen 72 XmlNode 40 XmlNodeList 42 XPath 59 XmlReader 15 XmlReadMode 118 XmlResolver 82 XML-Schema 68, 96 attributeFormDefault 75 aus Tabellen 105 elementFormDefault 75 erstellen 98 Grundlagen 89 Namensräume 68 Visual Studio-Designer 101

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

.NET Essentials

XmlSchemaCollection 75 Add 109 XmlSerializer 36 XML 27 XmlTextReader 16, 23 XmlTextWriter, Namensräume 70 XmlUrlResolver 81 XmlValidatingReader 92 Schemas-Auflistung 111 XmlWriter 15 Namensräume 70 XPath 53 Achsen 55 Ausdrücke 53 Evaluate 63 Funktionen 57 Grundlagen 53 Kontextknoten 55 Namensräume 74 Prädikate 56 Select-Methode 62 XPathNavigator 61 XPath-Abfrage, SelectNodes 59 XPath-Ausdrücke, kompilieren 64 XPathDocument 61 XPathExpression 64 SetContext 74 XPathNavigable 87 XPathNavigator 61 Clone 63 CreateNavigator 61 Matches 61 MoveTo 61 MoveToAttribute 61 MoveToFirst 62

MoveToFirstAttribute 62 MoveToFirstChild 62 MoveToFirstNamespace 62 MoveToId 62 MoveToNamespace 62 MoveToNext 62 MoveToNextAttribute 62 MoveToNextNamespace 62 MoveToParent 62 MoveToPrevious 62 MoveToRoot 62 Navigationsmethoden 61 XPathNodeIterator 62 Current 63 XSD 96 Grundlagen 96 xsd, Klassen erzeugen 30 xsd.exe Datenschema 105 Klassenschema 108 Schema erstellen 98 XSD-Schema 109 DataSet erstellen 115 XSD-Validierung 109 XSL 79 Stylesheet 79 Templates 80 XML-Strukturtransformation 84 XPath-Ausdrücke 80 XSL-Stylesheets 87 Aufbau 80 laden 81 XSLT 79 XslTransform 82 XSL-Transformationen 79 verketten 86

139

T H E

S I G N

O F

E X C E L L E N C E

Go To C# Armin Hanisch

C# ist das Rückgrat von Microsofts .NET-Strategie. Weil Ihre Zeit kostbar ist, führt Armin Hanisch Sie auf kurzen Wegen in C# und das .NET-Framework ein. Lernen Sie alles Wichtige: .NET, C#, zeitgemäße Softwareentwicklung mit OOP, dazu jede Menge Codebeispiele, Aufgaben und Fallstudien aus der Praxis zum Üben und eine Referenz. Go To 534 Seiten, 1 CD-ROM € 39,95 [D] / € 41,10 [A] ISBN 3-8273-1932-3

T H E

S I G N

O F

E X C E L L E N C E

Visual Basic .NET Grundlagen, Programmiertechniken, Windows-Anwendungen Michael Kofler

Sind Sie professioneller Programmierer? Suchen Sie nach einem soliden Fundament für die VB.NET-Programmierung im bewährten und preisgekrönten Kofler-Schreibstil? Möchten Sie eine tiefgehende Einführung in die wichtigsten .NET-Bibliotheken? Dann ist das das richtige Buch für Sie: Schwerpunkte sind die Neuerungen gegenüber VB6, Variablen- und Objektverwaltung, objektorientierte Programmierung, Umgang mit Dateien, Windows-Programmierung, Windows.Forms sowie Grafik und Drucken (GDI+). Praxisnahe Beispiele veranschaulichen die Entwicklung von .NET-Projekten. Programmer’s Choice 1076 Seiten, 1 CD-ROM € 49,95 [D] / € 51,40 [A] ISBN 3-8273-1982-X

T H E

S I G N

O F

E X C E L L E N C E

Essential XML XML für die Softwareentwicklung Don Box, Aaron Skonnard, John Lam

XML für die Softwareentwicklung. Das Buch geht über XML als »Markup language« hinaus und zeigt, wie man es – wie COM oder CORBA als Technologie für die Integration von Komponenten verwenden kann. Dabei werden alle wichtigen Themen und Techniken ausführlich dargestellt, die für ein tieferes Verständnis für die mächtigen Möglicheiten von XML notwendig sind. Programmer’s Choice 320 Seiten € 44,95 [D] / € 46,30 [A] ISBN 3-8273-1769-X

T H E

S I G N

O F

E X C E L L E N C E

XML Web ServiceAnwendungen mit Microsoft.NET Christian Weyer

Was ist so besonders an .NET? Und wie können XML Web Services mir helfen? Wenn Sie sich diese Fragen stellen, finden Sie hier die Antworten: Microsoft läutet mit XML-basierten Web Services auf Basis von .NET eine neue Ära im Bereich von Internet- und Intranetanwendungen ein. Das Buch klärt das »Warum?« und »Wie?« hinter der neuen Architektur von .NET und XML Web Services und zeigt, welch starkes Team die beiden zusammen bilden. An vielen Beispielen sehen Sie, wie die einzelnen Techniken zusammenwirken, und lernen eigene XML Web Service-Anwendungen zu erstellen. net.com 318 Seiten, 1 CD-ROM € 39,95 [D] / € 41,10 [A] ISBN 3-8273-1891-2