Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 16 XML
Pfeil 16.1 XML-Dokumente
Pfeil 16.1.1 Wohlgeformte und gültige XML-Dokumente
Pfeil 16.1.2 Regeln für wohlgeformten XML-Code
Pfeil 16.1.3 Kommentare
Pfeil 16.1.4 Verarbeitungsanweisungen
Pfeil 16.1.5 Reservierte Zeichen in XML
Pfeil 16.1.6 CDATA-Abschnitte
Pfeil 16.1.7 Namensräume (Namespaces)
Pfeil 16.2 Gültigkeit eines XML-Dokuments
Pfeil 16.2.1 XML Schema (XSD)
Pfeil 16.2.2 XML-Dokument mit einem XML Schema verknüpfen
Pfeil 16.2.3 Struktur eines XML Schemas
Pfeil 16.3 Die Klasse »XmlReader«
Pfeil 16.3.1 XML-Dokumente mit einem »XmlReader«-Objekt lesen
Pfeil 16.3.2 Validieren eines XML-Dokuments
Pfeil 16.4 Eigenschaften und Methoden der Klasse »XmlReader«
Pfeil 16.4.1 Navigation mit dem »XmlReader«
Pfeil 16.4.2 Eigenschaften und Methoden im Zusammenhang mit Attributen
Pfeil 16.4.3 Eigenschaften und Methoden im Zusammenhang mit Namespaces
Pfeil 16.4.4 Daten lesen
Pfeil 16.5 Die Klasse »XmlWriter«
Pfeil 16.5.1 Die Methoden der Klasse »XmlWriter«
Pfeil 16.6 Navigation durch XML (XPath)
Pfeil 16.6.1 Die Klasse »XPathNavigator«
Pfeil 16.6.2 XPath-Ausdrücke
Pfeil 16.6.3 Kontextknoten
Pfeil 16.6.4 Beispiele mit XPath-Ausdrücken
Pfeil 16.6.5 Knotenmengen mit der »Select«-Methode
Pfeil 16.6.6 Auswerten von XPath-Ausdrücken
Pfeil 16.7 Document Object Model (DOM)
Pfeil 16.7.1 Allgemeines
Pfeil 16.7.2 Arbeiten mit XmlDocument
Pfeil 16.7.3 XmlDocument und XPathNavigator
Pfeil 16.7.4 Die Klasse »XmlNode« (Operationen mit Knoten)
Pfeil 16.7.5 XML-Struktur manipulieren
Pfeil 16.7.6 Knoten ändern
Pfeil 16.7.7 Löschen in einem XML-Dokument
Pfeil 16.8 Serialisierung mit »XmlSerializer«
Pfeil 16.8.1 XML-Serialisierung mit Attributen steuern
Pfeil 16.9 LINQ to XML
Pfeil 16.9.1 Klassenhierarchie von LINQ to XML
Pfeil 16.9.2 Die Klasse »XElement«
Pfeil 16.9.3 Die Klasse »XDocument«
Pfeil 16.9.4 Navigation im XML-Dokument
Pfeil 16.9.5 Änderungen am XML-Dokument vornehmen


Galileo Computing - Zum Seitenanfang

16.6 Navigation durch XML (XPath) Zur nächsten ÜberschriftZur vorigen Überschrift


Galileo Computing - Zum Seitenanfang

16.6.1 Die Klasse »XPathNavigator« Zur nächsten ÜberschriftZur vorigen Überschrift

Der XmlReader ermöglicht nur eine Vorwärtsbewegung durch ein XML-Dokument. In manchen Situationen mag das durchaus genügen, aber häufig wird man nach bestimmten Elementen und den von ihnen beschriebenen Daten suchen und dabei beliebig navigieren müssen. Hierbei werden wir von einem Objekt vom Typ XPathNavigator unterstützt, das zum Namensraum System.Xml.XPath gehört. Mit einem XPathNavigator können Sie zum Beispiel auch rückwärts navigieren und darüber hinaus Suchmuster angeben, die ein Filtern der Daten ermöglichen. Diese Suchmuster werden als XPath-Ausdrücke bezeichnet. Die XPathNavigator-Klasse unterstützt die Funktionen von XPath 2.0.

Das hört sich sehr positiv an, ist aber mit einem Nachteil verbunden, denn das ganze XML-Dokument muss zuerst in den Speicher geladen werden. Sie müssen also einen Verlust an Performance und je nach Größe des XML-Dokuments auch eine vergleichsweise hohe Speicherbelastung akzeptieren.

Ein XPathNavigator kann auf ausgehend von

  • einem XPathDocument-Objekt oder
  • einem XmlDocument-Objekt

mit der Methode CreateNavigator erstellt werden. Ein XPathDocument-Objekt dient nur dem schnellen Einlesen eines XML-Dokuments; die Daten sind schreibgeschützt und erlauben keinerlei weitere Verarbeitung. Daten hingegen, die ein XmlDocument-Objekt zur Verfügung stellt, können auch verändert werden. Auf die zahlreichen Möglichkeiten, die sich hinter XmlDocument verbergen, kommen wir im nächsten Abschnitt zu sprechen.

Um die Vorteile eines XPathNavigator-Objekts nutzen zu können, benötigen Sie zuerst ein XPathDocument-Objekt (oder alternativ ein XmlDocument-Objekt). Dem Konstruktor können Sie den Pfad zu der XML-Datei oder einen Stream übergeben, der die XML-Daten liefert. Anschließend rufen Sie die Methode CreateNavigator auf, die die Referenz auf ein XPathNavigator-Objekt liefert.


XPathDocument xPathDoc = new XPathDocument(@"D:\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();

Jetzt können die Methoden des XPathNavigator-Objekts zur Navigation des sich im Speicher befindlichen XML-Dokuments benutzt werden. Dabei gilt es, zwei Techniken zu unterscheiden:

  • Navigation mit den Move-Methoden
  • Navigation unter Zuhilfenahme von XPath-Ausdrücken

Die zahlreichen Move-Methoden gestatten die beliebige Navigation durch ein XML-Dokument. Allerdings ist der Code insbesondere bei komplexen XML-Strukturen oft nur schwierig nachvollziehbar. Greifen Sie auf XPath-Ausdrücke zurück, wird der Code kürzer und hat dabei auch noch den Vorteil, nach mehreren Elementen gleichzeitig suchen zu können. Nachteilig dabei ist, dass der Einsatz von XPath-Ausdrücken voraussetzt, dass Sie sich zuvor mit der Syntax von XPath anfreunden. Eine Einführung dazu erhalten Sie später.

Navigieren mit den »Move«-Methoden

XPathNavigator verfolgt ein Cursor-basiertes Modell, bei dem eine Art Zeiger immer auf einen Knoten im XML-Dokument zeigt. Ausgehend von dem aktuellen Knoten, der auch als Kontextknoten bezeichnet wird, kann man mit einer der zahlreichen Methoden beliebig durch das Dokument navigieren.

An dieser Stelle alle Methoden und Eigenschaften eines XPathNavigator-Objekts vollständig zu beschreiben, würde den Rahmen sprengen. Daher möchte ich mich auf ein paar wenige Navigationsmethoden beschränken, um Ihnen ein Gefühl dafür zu vermitteln, welche Möglichkeiten in dieser Klasse stecken.


Tabelle 16.9 Die »Move«-Methoden eines »XPathNavigator«-Objekts (Auszug)

Methode Beschreibung

MoveTo

Dieser Methode wird eine XPathNavigator-Instanz übergeben, die auf dem Knoten positioniert ist, zu dem der aktuelle Knoten wechseln soll.

MoveToFirstAttribute

Verschiebt den XPathNavigator beim Überschreiben in einer abgeleiteten Klasse auf das erste Attribut des aktuellen Knotens.

MoveToAttribute

Verschiebt den XPathNavigator zu dem Attribut mit dem angegebenen Namen und Namespace-URI.

MoveToRoot

Verschiebt die Position des Cursors auf den Stammknoten.

MoveToFirst

Verschiebt den Cursor auf den ersten nebengeordneten Knoten des aktuellen Knotens. Der Rückgabewert ist true, wenn es einen nebengeordneten Knoten gibt, ansonsten false.

MoveToNext

Verschiebt den Cursor auf den nächsten nebengeordneten Knoten des aktuellen Knotens. Der Rückgabewert ist true, wenn es einen nebengeordneten Knoten gibt, ansonsten false.

MoveToPrevious

Verschiebt den Cursor auf den vorhergehenden nebengeordneten Knoten des aktuellen Knotens. Der Rückgabewert ist true, wenn es einen nebengeordneten Knoten gibt, ansonsten false.

MoveToChild

Verschiebt den Cursor auf den angegebenen untergeordneten Knoten. Der Rückgabewert ist true, wenn es den untergeordneten Knoten gibt, ansonsten false.

MoveToFirstChild

Verschiebt den Cursor auf den ersten untergeordneten Knoten des aktuellen Knotens. Der Rückgabewert ist true, wenn es einen untergeordneten Knoten gibt, ansonsten false.

MoveToFollowing

Verschiebt den Cursor auf das Element mit dem angegebenen lokalen Namen oder einen bestimmten XPathNodeType.

MoveToParent

Verschiebt den Cursor auf den übergeordneten Knoten des aktuellen Knotens. Der Rückgabewert ist true, wenn die Aktion erfolgreich durchgeführt werden konnte, ansonsten false.


Wie Sie durch ein XML-Dokument navigieren, möchte ich Ihnen nun in Einzelschritten zeigen. Dabei dient wieder die Datei Personen.xml als Basis der einzulesenden XML-Struktur.

Nachdem Sie sich ein XPathNavigator-Objekt mit


XPathDocument xPathDoc = new XPathDocument(@"D:\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();

besorgt haben, müssen Sie den Cursor zuerst auf das Stammelement des XML-Dokuments positionieren. Dazu bieten sich die Methoden MoveToFollowing oder MoveToFirstChild an. Der Methode MoveToFollowing müssen Sie dabei ausdrücklich den Bezeichner des Stammelements übergeben, gegebenenfalls auch noch den Namespace-URI.


navigator.MoveToFollowing("Personen", "");

Einfacher ist die Handhabung der Methode MoveToFirstChild, die parameterlos ist.


navigator.MoveToFirstChild();

Damit steht der Cursor auf dem Stammelement Personen. Um zum ersten untergeordneten Knoten Person zu navigieren, muss MoveToFirstChild ein weiteres Mal aufgerufen werden.

Das Element Person hat mehrere untergeordnete Elemente. Möchte man diese der Reihe nach durchlaufen, muss der Cursor zuerst auf das erste untergeordnete Element, also Vorname, positioniert werden. Das bedeutet bereits den dritten Aufruf der Methode MoveToFirstChild hintereinander. Alle nebengeordneten Elemente, Zuname, Alter und Adresse, erhält man über jeweils einen Aufruf der Methode MoveToNext.

Zeigt der Cursor auf einen Person-Knoten, müssen Sie nicht alle Elemente Vorname, Zuname und Alter abrufen, um Adresse auszuwerten. Sie können dazu auch die Methode MoveToChild bemühen, der Sie den Bezeichner des gewünschten Elements sowie dessen Namespace-URI angeben.


navigator.MoveToChild("Adresse", "http://www.MyNS.de");

Ist das entsprechende Element keinem Namensraum zugeordnet, übergeben Sie eine leere Zeichenfolge.

Beispielprogramm

Sehen wir uns nun ein komplettes Beispiel an, in dem ein XML-Dokument vollständig durchlaufen wird. Dabei wird eine rekursive Pogrammiertechnik benutzt, die alle Knoten des Dokuments auswertet. Die Methode Navigate hat die Aufgabe, durch das Dokument zu navigieren, WriteNode gibt die Knotenbezeichner und die von ihnen beschriebenen Werte aus.


// ------------------------------------------------------
// Beispiel: ...\Kapitel 16\XPathNavigatorSample
// ------------------------------------------------------
static void Main(string[] args) {
  XPathDocument doc = new XPathDocument(@"..\..\Personen.xml");
  XPathNavigator navi = doc.CreateNavigator();
  // zum Stammknoten navigieren
  navi.MoveToRoot();
  Navigate(navi);
  Console.ReadLine();
}
// Methode, die durch die Knoten navigiert
static void Navigate(XPathNavigator navi) {
  WriteNode(navi); 
  // verschiebt den Cursor auf den ersten untergeordneten Knoten 
  if (navi.MoveToFirstChild()) {
  // verschiebt den Cursor zum nächsten nebengeordneten Knoten
    do {
      Navigate(navi);
    } while (navi.MoveToNext());
    navi.MoveToParent();
  }
}
// Methode zur Ausgabe an der Konsole
static void WriteNode(XPathNavigator navi) {
  switch (navi.NodeType)
  {
    case XPathNodeType.Element:
      if (navi.HasAttributes) {
        Console.Write("<" + navi.Name + " ");
        navi.MoveToFirstAttribute();
        do {
          Console.Write(navi.Name + "=" + navi.Value + " ");
        } while (navi.MoveToNextAttribute());
        Console.WriteLine(">");
        navi.MoveToParent();
      }
      else {
        Console.WriteLine("<" + navi.Name + ">");
      }
      break;
    case XPathNodeType.Text:
      Console.WriteLine(navi.Value);
      break;
  }
}


Galileo Computing - Zum Seitenanfang

16.6.2 XPath-Ausdrücke Zur nächsten ÜberschriftZur vorigen Überschrift

XPath ist eine Entwicklung des W3-Konsortiums und dient dazu, nach einem oder mehreren Knoten in einer XML-Struktur zu suchen. XPath wird normalerweise im Zusammenhang mit anderen Standards eingesetzt und stellt eine XML-Struktur als einen Baum dar, der aus Knoten besteht. Die Lokalisierung eines oder mehrerer Knoten ähnelt der Notation einer URL – vermutlich stammt auch daher der Name XPath. Für die effiziente Arbeit mit XML ist XPath unabdingbar. Daher wollen wir uns in diesem Abschnitt auch ein wenig mit der XPath-Spezifikation beschäftigen. Dabei auf jedes Detail einzugehen, würde den Rahmen dieses Kapitels sprengen. Wenn Sie aber über diesen Abschnitt hinaus nach weiteren Informationen suchen, finden Sie diese unter http://www.w3.org/TR/xpath.html.

Aber wie sieht ein XPath-Ausdruck aus? Nehmen wir an, Sie interessieren sich für alle Zunamen in dem XML-Dokument, das durch die Datei Personen.xml beschrieben wird. Mit dem folgenden Codefragment können Sie das bereits erreichen.


XPathDocument xPathDoc = new XPathDocument(@"..\..\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();
navigator.MoveToFirstChild();
XPathNodeIterator iterator = navigator.Select("//Zuname");
while (iterator.MoveNext())
{
  Console.WriteLine(iterator.Current.Value);
}

Hätten Sie nur die Methoden der Klasse XPathNavigator benutzt, wäre der Code deutlich aufwendiger ausgefallen. Der XPath-Ausdruck //Zuname vereinfacht den Zugriff auf die Zunamen sehr deutlich.

Wie bereits erwähnt, stellt XPath eine XML-Dokumentstruktur als Baum dar. Dabei gibt es verschiedene Knotentypen, zum Beispiel Elementknoten, Textknoten oder Attributknoten. Die Lokalisierung der verschiedenen Ebenen besteht aus maximal drei Teilen:

  • einer Achse, die zum Navigieren innerhalb der XML-Struktur dient
  • einer Knotenprüfung, um weitere Kriterien zu definieren. Damit lassen sich bestimmte Knoten selektieren.
  • einem Prädikat, um die selektierten Knoten weiter filtern zu können

Die Angabe von Achse und Knotenprüfung ist vorgeschrieben, während ein Prädikat optional ist. Die allgemeine Syntax eines XPath-Ausdrucks lautet:


achse::knotenprüfung[prädikat]

Beschreibt ein XPath-Ausdruck ein Prädikat, dann wird es in eckige Klammern eingeschlossen.


Galileo Computing - Zum Seitenanfang

16.6.3 Kontextknoten Zur nächsten ÜberschriftZur vorigen Überschrift

Das Resultat einer XPath-Abfrage hängt davon ab, von wo die Suche gestartet wird. Startet die Suche beim Wurzelelement, kann das Ergebnis ein ganz anderes sein, als würde die Suche bei einem Unterelement beginnen. Daher ist das Startelement von ganz wesentlicher Bedeutung. XPath verwendet das Konzept eines sogenannten Kontextknotens. Dieser Kontextknoten ist der aktuelle Knoten, an dem die Suche gestartet wird.

Achsen

Bei der Ausführung einer XPath-Abfrage kann eine bestimmte Richtung vorgeschrieben werden. Diese wird als Achse bezeichnet. Eine Analogie findet sich im Dateisystem, denn Sie können beispielsweise mit .. zum übergeordneten Verzeichnis wechseln.

Da einige Achsen sehr häufig benötigt werden, besitzen sie eine Abkürzung. In Tabelle 16.10 sind die wichtigsten Achsen beschrieben.


Tabelle 16.10 Achsen in XPath-Ausdrücken

Achse Beschreibung

attribute

Hiermit werden die Attribute des Kontextknotens bestimmt (Abkürzung: @).

child

Mit dieser Achse werden die dem Kontextknoten untergeordneten Elemente (Nachkommen) beschrieben.

descendant

Irgendein untergeordnetes Element des Kontextknotens. Dabei kann es sich auch um einen Nachkommen über zwei oder mehr Ebenen handeln.

descendant-or-self

Wie descendant, jedoch unter Einbeziehung des Kontextknotens (Abkürzung: //).

following

Mit dieser Achse werden die Knoten angesprochen, die sich auf der Ebene des Kontextknotens befinden und diesem nachfolgen. Zu der Ergebnismenge gehören auch die untergeordneten Elemente der gefundenen Knoten.

parent

Das dem Kontextknoten übergeordnete Element (Abkürzung: ..).

preceding

Vorherige Knoten des Kontextknotens auf gleicher Ebene, einschließlich der ihnen untergeordneten Knoten


Beginnt ein XPath-Ausdruck nicht mit / oder //, beginnt die Suche immer an der aktuellen Curorposition. Die Suche beschränkt sich dann auf die Elemente des Teils der XML-Struktur, der an der Cursorposition beginnt.

Knotenprüfungen

Ein weiteres Kriterium zur Filterung eines Elements können Sie nach der Angabe der Achse festlegen. Es handelt sich um die Knotenprüfung, die anhand der Achse eine Auswahl der zu selektierenden Elemente trifft. Die Angabe der Prüfung kann direkt der Elementname sein.


Tabelle 16.11 Knotenprüfungen in XPath-Ausdrücken

Knotentyp Beschreibung

node()

Es werden alle Knoten, auch unter Einbeziehung von Namensraum- und Attributknoten, ausgewählt.

comment()

Auswahl eines Kommentarknotens

text()

Auswahl von Knoten, die einen Text enthalten

processing-instruction()

Auswahl der Prozessoranweisungen im XML-Dokument


Prädikate

Auf allen Knoten, die die Ergebnismenge nach der Achsenbestimmung und der Knotenprüfung bilden, können Sie spezielle Prädikate anwenden, um die selektierten Knoten weiter zu filtern:

  • logische Operatoren: or, and, !, <, >, <=, >=, =, !=
  • arithmetische Operatoren: +, -, div, *, mod

Da die Zeichen < und > in XML eine besondere Bedeutung haben, müssen Sie sie durch &lt; sowie &gt; ersetzen.


Galileo Computing - Zum Seitenanfang

16.6.4 Beispiele mit XPath-Ausdrücken Zur nächsten ÜberschriftZur vorigen Überschrift

Nachfolgend zeige ich Ihnen einige XPath-Ausdrücke und deren Ergebnismenge. Alle Beispiele basieren auf einem XML-Dokument, in dem insgesamt vier Person-Elemente definiert sind und zusätzlich noch das Element Mitarbeiter. Das Stammelement lautet auch hier Personen.


<Personen>
  <Person>
    <Vorname>Manfred</Vorname>
    <Zuname>Fischer</Zuname>
    <Alter>45</Alter>
    <Adresse Ort="Bonn" Strasse="Neuestr.34"></Adresse>
  </Person>
  ...
  <Mitarbeiter>
    <Vorname>Peter</Vorname>
    <Zuname>Goldbach</Zuname>
    <Position>Chef</Position>
  </Mitarbeiter>
</Personen>

Beispiel 1

Die Auswertung des folgenden Positionspfades liefert alle Person- und Mitarbeiter-Elemente, einschließlich der den Elementen Person und Mitarbeiter untergeordneten Elemente, jedoch ohne die Attribute. In der Ergebnisliste steht nur ein Gesamtergebnis. Das Ergebnis ist unabhängig von der Cursorposition.


//Personen

Die Achse // ist sehr nützlich, um Elemente unabhängig von ihrer Position in der Struktur zu finden.

Beispiel 2

Zum gleichen Ergebnis führt auch der Ausdruck


/Personen

Zu keinem Ergebnis führt jedoch die Auswertung des folgenden Aufrufs, da hier die Angabe des Stammelements fehlt:


/Person 

Anders hingegen das nächste Beispiel. Der Ausdruck beschreibt alle Person-Elemente, unabhängig von deren Position innerhalb der XML-Struktur. In der Ergebnisliste steht für jede Person ein separates Resultat. Da das XML-Dokument vier Personen beschreibt, liegen somit auch vier Teilergebnisse vor.


//Person

Möchten Sie vielleicht auf ein bestimmtes Person-Element zugreifen, dessen Position Sie im XML-Dokument kennen? Der nächste XPath-Ausdruck liefert das dritte Person-Element zurück.


//Person[3]

XPath unterstützt auch Platzhalter. Der folgende Ausdruck wählt alle Elemente aus, die dem Stammelement Personen untergeordnet sind. Es liegen auch hier fünf Resultate in der Ergebnismenge vor, da sowohl Person als auch Mitarbeiter ausgewertet werden. Besonders sinnvoll ist der Ausdruck, wenn dem Stammelement mehrere verschiedene Elemente untergeordnet sind und alle in der Ergebnismenge stehen sollen.


/Personen/*

Den Platzhalter können Sie auch angeben, um auf die Attribute des Elements Adresse zuzugreifen:


/Personen/Person/Adresse/@*

Interessieren hingegen nur die Werte, die durch das Attribut Ort beschrieben werden, geben Sie den Attributsbezeichner anstelle des Platzhalters an;


/Personen/Person/Adresse/@Ort

Nehmen wir nun an, der Positionscursor zeige auf das erste Element Person in der XML-Struktur. Mit dem XPath-Ausdruck


Vorname

wird als Ergebnis Manfred ausgegeben. Wird explizit keine Achse angegeben, gilt child als Vorgabe. Somit führt der gezeigte Ausdruck zum gleichen Ergebnis wie:


child::Vorname

Hätte der Cursor eine andere Position eingenommen, wäre die Ergebnismenge leer.

Unabhängig von der Cursorposition wird der nächste XPath-Ausdruck alle Vornamen ausgeben, da er gleichbedeutend mit dem XPath-Ausdruck Vorname ist:


//child::Vorname

Mit


/child::Vorname

bleibt die Ergebnismenge jedoch leer.

Mit optionalen Prädikaten lassen sich Ergebnismengen weiter filtern. Sie sind Teil des Positionspfades, der in eckigen Klammern erscheint. Der nächste Ausdruck verwendet ein Prädikat, um die Auswahl der Person-Elemente auf die Personen einzugrenzen, deren Alter größer 40 ist:


//Person[Alter > 40]

Sehr ähnlich wird mit dem folgenden Ausdruck gefiltert. Hierbei interessieren uns alle Personen, die in Bonn wohnen. Da der Ort als Attribut definiert ist, muss auf den Attributinhalt zugegriffen werden.


//Person/Adresse[@Ort = 'Bonn']

Prädikate dürfen gemäß XPath-Spezifikation auch mit and oder or verknüpft werden. Im folgenden XPath-Ausdruck werden die beiden zuvor gezeigten Ausdrücke benutzt, um alle Person-Elemente zu finden, die sowohl älter als 40 sind als auch in Bonn wohnen.


//Person[Alter > 40 and Adresse[@Ort = 'Bonn']]

XPath unterstützt integrierte Funktionen, die häufig in Prädikaten verwendet werden. Die Auswertung des folgenden Ausdrucks ergibt alle Person-Elemente, deren Vorname mit dem Buchstaben »P« beginnt.


//Person[starts-with(Vorname, 'P')]

Zum Abschluss der XPath-Beispiele möchte ich Ihnen noch zeigen, wie der Ausdruck lauten muss, um nach allen Personen innerhalb der XML-Struktur zu suchen, deren Zuname »Schmidt« oder »Meier« lautet.


//Zuname[text()='Meier' or text()='Schmidt']

Die bis hier gezeigten Beispiele können vielleicht einen Eindruck davon vermitteln, wie mächtig XPath-Ausdrücke formuliert werden können. Alle Möglichkeiten hier zu zeigen, ist aus Platzgründen nicht möglich. Für das weitere Vorgehen und das Verständnis der folgenden Beispiele reichen die Ausführungen aber aus. Wenn Sie intensiver in das Thema XPath einsteigen wollen, empfiehlt es sich, einen Blick in die Originalspezifikation zu werfen, deren Link Sie am Anfang des Kapitels finden.


Galileo Computing - Zum Seitenanfang

16.6.5 Knotenmengen mit der »Select«-Methode Zur nächsten ÜberschriftZur vorigen Überschrift

In Abschnitt 16.6.1 haben Sie gesehen, wie Sie mit den Methoden der Klasse XPathNavigator durch die Struktur einer XML-Vorlage navigieren können. Mit den Kenntnissen von XPath-Ausdrücken ausgestattet, lässt sich ein XPathNavigator-Objekt aber noch effektiver einsetzen.

Um einen XPath-Ausdruck gegen ein XML-Dokument abzusetzen, steht Ihnen die Methode Select zur Verfügung. Das Ergebnis des Methodenaufrufs ist eine Menge von Knoten, die von einem XPathNodeIterator-Objekt beschrieben werden. Der XPathNodeIterator ist zunächst auf dem Kontextknoten positioniert, der der Ausgangspunkt der XPath-Abfrage ist. Um zum ersten Knoten in der Ergebnismenge zu gelangen, muss daher zuerst MoveNext auf das Iterator-Objekt aufgerufen werden. MoveNext eignet sich sehr gut, um in einer Schleife eingesetzt zu werden, da der Rückgabewert true ist, falls noch ein weiterer Knoten in der Ergebnismenge vorliegt.


XPathDocument xPathDoc = new XPathDocument(@"C:\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();
XPathNodeIterator iter = navigator.Select("//Person");
while (iter.MoveNext()) {
  Console.WriteLine(iter.Current.Value);
}

Die Eigenschaft Current des Iterators ist schreibgeschützt und liefert ein neues XPathNavigator-Objekt, das auf das aktuelle XML-Element in der Ergebnismenge zeigt:


public XPathNavigator Current {get;}

Current nicht nur wichtig für die Auswertung des gefundenen Knotens, sondern kann darüber hinaus auch Ausgangspunkt für weitere Suchoperationen sein.

Das XPathNodeIterator-Objekt hat neben Current noch zwei weitere interessante Eigenschaften: Count und CurrentPosition. Count liefert die Anzahl der Knoten in der Ergebnismenge und CurrentPosition den Index der aktuellen Position in der Ergebnismenge. Aber Vorsicht, denn der Index ist nicht 0-basiert, sondern 1-basiert. Der Index 0 gibt nämlich an, dass keine Knoten ausgewählt sind.

Ändern des Kontextknotens

Das XPathNavigator-Objekt, auf das die Select-Methode aufgerufen worden ist, ändert seine Position auch dann nicht, wenn die Ergebnismenge durchlaufen wird. Der Kontextknoten bleibt also gleich. Wenn der XPath-Ausdruck nicht mit / oder // beginnt, beginnt die Suche nach den Elementen an der aktuellen Cursorposition des XPathNavigator-Objekts.

Um die Cursorposition auf eines der Suchergebnisse zu setzen, können Sie statt der Methode Select die Methode SelectSingleNode des XPathNavigator-Objekts einsetzen. Der Rückgabewert ist ein neues XPathNavigator-Objekt, das Sie der MoveTo-Methode übergeben.


bool result =
  navigator.MoveTo(navigator.SelectSingleNode("XPath-Ausdruck"));

Der Aufruf liefert true, wenn die Position des Navigators verschoben werden konnte. Bei false bleibt die Cursorposition unverändert.

Weitere »Select«-Methoden

Neben Select gibt es mit

  • SelectAncestors
  • SelectChildren
  • SelectDescendants

noch drei weitere Varianten zur Filterung von XML-Elementen. SelectAncestors wählt alle übergeordneten Knoten des aktuellen Knotens aus, SelectChildren alle direkt untergeordneten Knoten und SelectDescendants ausnahmslos alle untergeordneten Knoten. Allen drei genannten Methoden können Sie bestimmte Kriterien für die Filterung übergeben: Entweder Sie geben den Namen des gesuchten Elements an oder den Knotentyp (XmlNodeType).

Das folgende Beispiel zeigt, wie die genannten Select-Methoden benutzt werden können. Als Grundlage dazu dient uns die Datei Personen.xml, die jedoch um das zusätzliche Element Geschlecht ergänzt worden ist, das Zuname untergeordnet ist.


<Person>
  <Vorname>Manfred</Vorname>
  <Zuname>Fischer
    <Geschlecht>Männlich</Geschlecht>
  </Zuname>
  <Alter>45</Alter>
  <Adresse Ort="Bonn" Strasse="Neuestr.34"></Adresse>
</Person>

Hier nun der Code des Beispielprogramms:


// ---------------------------------------------------------
// Beispiel: ...\Kapitel 16\SelectMethoden
// ---------------------------------------------------------
string trenner = new string('-', 45);
XPathDocument doc = new XPathDocument("..\\..\\Personen.xml");
XPathNavigator navigator = doc.CreateNavigator();
navigator.MoveToChild("Personen","");
navigator.MoveToChild("Person", "");
// alle untergeordneten Elemente einer 'Person'
XPathNodeIterator descendant = 
navigator.SelectDescendants("", "", false);
Console.WriteLine("Alle untergeordneten Elemente von 'Person':");
Console.WriteLine(trenner);
while (descendant.MoveNext()) {
  Console.WriteLine(descendant.Current.Name);
}
// alle direkt untergeordneten Elemente einer 'Person'
XPathNodeIterator children = navigator.SelectChildren("", "");
Console.WriteLine("\nDirekt untergeordnete Elemente von 'Person':");
Console.WriteLine(trenner);
while (children.MoveNext()) {
  Console.WriteLine(children.Current.Name);
}
// die übergeordneten Elemente von 'Zuname'
navigator.MoveToChild("Zuname", "");
XPathNodeIterator ancestors = navigator.SelectAncestors("", "", false);
Console.WriteLine("\nÜbergeordnete Elemente von 'Zuname':");
Console.WriteLine(trenner); 
while (ancestors.MoveNext()) {
  Console.WriteLine(ancestors.Current.Name);
}

Abbildung 16.6 Ergebnisliste des Beispiels »SelectMethoden«

Als Grundlage der Select-Methoden wird jeweils die Überladung benutzt, die im ersten Parameter den Knotenbezeichner erwartet. Gibt man eine leere Zeichenfolge an, werden der Ergebnismenge alle gefundenen Knoten hinzugefügt. Der zweite Parameter erwartet den Namespace-URI. Da in unserem XML-Dokument kein Namespace definiert ist, wird hier eine leere Zeichenfolge übergeben. Der dritte Parameter der Methoden SelectAncestors und SelectDescendants schließlich erwartet einen booleschen Wert, der angibt, ob der aktuelle Knoten (der Kontextknoten) in die Ergebnismenge aufgenommen werden soll. In beiden Fällen wurde unter Angabe von false darauf verzichtet.

Kompilieren von XPath-Ausdrücken

Bei der Filterung von Knoten mit XPath-Ausdrücken führt der Parser immer wieder eine Analyse und Optimierung der Abfrage durch. Bei Abfragen, die wiederholt verwendet werden, bedeutet das eine Leistungseinbuße. Um dem zu begegnen, besitzen die Methoden, die einen XPath-Ausdruck akzeptieren, auch eine Überladung für einen kompilierten Ausdruck, der durch XPathExpression dargestellt wird. Dazu zählt auch Select.

Ein XPathExpression-Objekt erhalten Sie als Rückgabewert der statischen Methode Compile der Klasse XPathExpression.


public static XPathExpression Compile(string xpath)

Der Einsatz eines kompilierten XPath-Ausdrucks kann insbesondere bei komplexen Ausdrücken eine durchaus deutliche Steigerung der Abfrageleistung bringen.


XPathDocument xPathDoc = new XPathDocument(@"D:\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();
XPathExpression xpathExpr = XPathExpression.Compile("//Person");
XPathNodeIterator iter = navigator.Select(xpathExpr);
while (iter.MoveNext()) {
  Console.WriteLine(iter.Current.Value);
}


Galileo Computing - Zum Seitenanfang

16.6.6 Auswerten von XPath-Ausdrücken topZur vorigen Überschrift

Die Methode »Evaluate«

Bisher habe ich Ihnen nur gezeigt, wie Sie mit XPath-Ausdrücken eine auf Knoten basierende Ergebnismenge auswerten können. XPath-Ausdrücke ermöglichen es aber auch, Berechnungen auszuführen. Dabei hilft die Methode Evaluate des XPathNavigator-Objekts weiter:


public virtual Object Evaluate(string xpath)

Es gibt weitere Überladungen, die auch ein XPathExpression- oder XPathNodeIterator-Objekt entgegennehmen.

Dazu sehen wir uns direkt ein Beispiel an: Angenommen, Sie interessieren sich für das Durchschnittsalter aller Personen, die in der Datei Personen.xml beschrieben werden. Der XPath-Ausdruck dazu lautet:


sum(//Alter) div count(//Alter)

Die Funktion sum liefert die Summe der in der Ergebnismenge //Alter enthaltenen Werte, und count liefert die Anzahl der zurückgegebenen Ergebnisse aus der Abfrage //Alter. Die Funktion div ist der Divisionsoperator.


string xPath = "sum(//Alter) div count(//Alter)";
XPathDocument xPathDoc = new XPathDocument(@"D:\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();
double result = Convert.ToDouble(navigator.Evaluate(xPath));
Console.WriteLine("Durchschnittsalter = {0}",result);

Da der Rückgabewert von Evaluate vom Typ Object ist, muss er noch in den passenden Typ konvertiert werden.

Die Methode »Matches«

Mit der Methode Matches können Sie feststellen, ob der aktuelle Knoten des XPathNavigators einem bestimmten XPath-Ausdruck entspricht.


public virtual bool Matches(string xpath)

Matches akzeptiert auch einen kompilierten XPath-Ausdruck vom Typ XPathExpression.

Im folgenden Codefragment wird untersucht, ob der Cursor das Personen-Element beschreibt. Der Rückgabewert ist in diesem Fall true, da zuvor mit MoveToFirstChild zum Stammelement des XML-Dokuments navigiert wird.


XPathDocument xPathDoc = new XPathDocument(@"D:\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();
navigator.MoveToFirstChild();
Console.WriteLine(navigator.Matches("//Personen").ToString());

Namensräume

In nahezu jedem XML-Dokument wird die Technik der Namensräume benutzt, um Elemente eindeutig zuordnen zu können. XPathNavigator unterstützt mit mehreren Eigenschaften und Methoden die Namensräume.


Tabelle 16.12 Eigenschaften und Methoden, die die Namespaces betreffen

Eigenschaft/Methode Beschreibung

LocalName

(Eigenschaft) Ruft den Bezeichner des aktuellen Knotens ohne Namespace-Präfix ab. Die ähnliche Eigenschaft Name liefert den Bezeichner samt Präfix.

NamespaceURI

(Eigenschaft) Ruft den Namespace-URI des aktuellen Knotens ab.

Prefix

(Eigenschaft) Ruft das Namespace-Präfix des aktuellen Knotens ab,

GetNamespacesInScope

(Methode) Gibt alle im Gültigkeitsbereich befindlichen Namespaces des aktuellen Knotens zurück.

LookupNamespace

(Methode) Ruft den Namespace-URI für das angegebene Präfix ab.

LookupPrefix

(Methode) Ruft das Namespace-Präfix für das angegebene Präfix ab.

MoveToFirstNamespace

(Methode) Verschiebt den XPathNavigator auf den ersten Namespace-Knoten des aktuellen Knotens.

MoveToNextNamespace

(Methode) Verschiebt den XPathNavigator auf den nächsten Namespace-Knoten des aktuellen Knotens.


Die Eigenschaften LocalName, NamespaceURI und Prefix sind schon vom XmlReader her bekannt und werden auf dem aktuellen Knoten ausgewertet. LookupNamespace und LookupPrefix liefern Informationen, die das gesamte XML-Dokument betreffen, aber nur auf ein Präfix oder einen Namensraum bezogen sind.

Die Methode GetNamespacesInScope müssen wir uns noch genauer ansehen, da ihr Einsatz etwas komplexer ist. Die Methode liefert alle Namespaces, die sich im Gültigkeitsbereich des aktuellen Knotens befinden. Ist der Navigator auf das Stammelement positioniert, handelt es sich folgerichtig um alle Namespaces des XML-Dokuments. Der Rückgabewert der Methode ist eine generische Collection, in der die einzelnen Elemente über ein Key/Value-Paar beschrieben werden. Jeder Namespace-Eintrag wird in der Collection durch den Typ KeyValuePair<string, string> beschrieben.

Sie müssen GetNamespacesInScope mitteilen, ob die gelieferte Liste gefiltert werden soll. Dazu übergeben Sie der Methode ein Argument vom Typ der Enumeration XmlNamespaceScope.


Tabelle 16.13 Konstanten der Enumeration »XmlNamespaceScope«

Enumerationswert Beschreibung

All

Liefert alle Namespaces, die sich im Gültigkeitsbereich des aktuellen Knotens befinden. Dies beinhaltet auch den xmlns:xml-Namespace, der immer implizit deklariert wird.

ExcludeXml

Liefert alle Namespaces, die im Gültigkeitsbereich des aktuellen Knotens definiert sind. Davon ausgeschlossen ist der xmlns:xml-Namespace, der immer implizit deklariert ist.

Local

Liefert alle Namespaces, die am aktuellen Knoten lokal definiert sind.


Im folgenden Codefragment wird die Liste der Namespaces eines XML-Dokuments abgefragt, ohne den impliziten xmlns:xml-Namespace zu berücksichtigen.


IDictionary<string, string> liste = 
  navi.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml);
foreach (KeyValuePair<string, string> temp in liste)
  Console.WriteLine("Key: {0,-3} Value: {1}", temp.Key, temp.Value);

Alternativ zu GetNamespacesInScope bieten sich auch die Methoden MoveToFirstNamespace und MoveToNextNamespace an, die beide auf den aktuellen Knoten bezogen sind. Beide Methoden liefern einen booleschen Wert zurück, der true ist, wenn der XPathNavigator auf den ersten bzw. nächsten Namespace positioniert werden kann.


if (navigator.MoveToFirstNamespace())
do {
  Console.WriteLine("Präfix: {0} - Namespace: {1} ", 
                     navigator.Name, navigator.Value);
} while (navigator.MoveToNextNamespace());

Beachten Sie, dass ein Präfix mit der Eigenschaft Name ausgewertet wird und der zugehörige Namensraum mit der Eigenschaft Value.

Namespaces in XPath-Ausdrücken

Eine etwas andere Behandlung erfahren die Namespaces, die in XPath-Ausdrücken verwendet und korrekt ausgewertet werden sollen. Grundsätzlich verlangt die XPath-Syntax, dass vor dem Elementnamen das Präfix angegeben wird, also:


Präfix:Elementbezeichner

Eine Überladung der Select-Methode des XPathNavigators berücksichtigt genau diesen Fall und schreibt ein Objekt mit IXmlNamespaceResolver vor. Diese Schnittstelle wird von der Klasse XmlNamespaceManager implementiert. Sie müssen also nur ein Objekt dieses Typs der Select-Methode übergeben.

Ein XmlNamespaceManager verwaltet alle Namespaces und die ihnen zugeordneten Präfixe. Dabei müssen die Elemente, die sich im Standardnamensraum befinden, besonders behandelt werden, denn in XML-Dokumenten ist diesen Elementen kein Präfix vorangestellt. Die XPath-Syntax verlangt aber auch in diesem Fall ein Präfix. Daher ist es erforderlich, dass Sie auch diesen Elementen ein Präfix zuordnen.

Das folgende Beispiel zeigt, wie Sie die Klasse XmlNamespaceManager einsetzen und einen Standard-Namespace definieren können. Beachten Sie dabei, dass jeder gefundene Namespace explizit dem XmlNamespaceManager-Objekt mit AddNamespace hinzugefügt werden muss.


// ---------------------------------------------------------
// Beispiel: ...\Kapitel 16\XPathWithNamespaces
// ---------------------------------------------------------
XPathDocument xPathDoc = new XPathDocument(@"..\..\Personen.xml");
XPathNavigator navigator = xPathDoc.CreateNavigator();
// XmlNamespaceManager instanziieren
XmlNamespaceManager mgr = 
              new XmlNamespaceManager(new NameTable());
navigator.MoveToRoot();
if (navigator.MoveToChild(XPathNodeType.Element)) {
  foreach (KeyValuePair<string, string> temp in 
      navigator.GetNamespacesInScope(XmlNamespaceScope.All)) 
    // Namespaces zum XmlNamespaceManager hinzufügen
    if (temp.Key == "")
      mgr.AddNamespace("default", temp.Value);
    else
      mgr.AddNamespace(temp.Key, temp.Value);
}
// Ausgabe aller Person-Elemente, die dem durch 'x'  
// beschriebenen Namespace zugeordnet werden
XPathNodeIterator iterator = navigator.Select("//x:Person", mgr);
while (iterator.MoveNext()) {
  Console.WriteLine(iterator.Current.Value);
}



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2010

Visual C# 2010
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2010
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern