## 16.7 Document Object Model (DOM) ### 16.7.1 Allgemeines Die Klasse XmlReader bietet nur einen schreibgeschützten vorwärtsgerichteten Lesezugriff, mit XmlWriter nur einen vorwärtsgerichteten Schreibzugriff. Zum Bearbeiten des Inhalts eines Elements, zum Einfügen neuer oder zum Löschen enthaltener Elemente gibt es weder Methoden noch Eigenschaften. Abhilfe schafft das Document Object Model, kurz DOM genannt. Hierbei handelt es sich um eine Spezifikation einer Schnittstelle für den beliebigen Zugriff auf die Elemente in einem XML-Dokument. Diese Spezifikation ist vom W3-Konsortium definiert worden. Beim Arbeiten auf Basis von DOM wird das komplette XML-Dokument in den Speicher geladen und wird in Form einer Baumstruktur dargestellt. Wenn XML in das Dokumentobjektmodell (DOM) eingelesen wird, werden die einzelnen Teile in Knoten umgesetzt. Diese enthalten zusätzliche Metadaten über sich selbst, z. B. ihren Knotentyp und ihre Werte. Der Knotentyp besteht aus seinem Objekt und bestimmt, welche Aktionen ausgeführt und welche Eigenschaften festgelegt oder abgerufen werden. Wie das intern realisiert wird, möchte ich Ihnen anhand des folgenden einfachen XML-Codes zeigen:
    <Person>
      <Name>Fischer</Name>
    </Person>
Dieser XML-Code wird im Speicher durch die in Abbildung 16.7 gezeigte Knotenstruktur dargestellt. Hier klicken, um das Bild zu vergrößern Abbildung 16.7 Eine einfache DOM-Struktur Das Person-Element wird zu einem XmlElement-Objekt, ebenso wie das folgende Element Name. Der Elementinhalt wird zu einem XmlText-Objekt. Die Methoden und Eigenschaften von XmlElement unterscheiden sich von denen, die für ein XmlText-Objekt verfügbar sind. Folglich ist es wichtig, zu wissen, in welchen Knotentyp das XML-Markup umgesetzt wird, da der Knotentyp bestimmt, welche Aktionen ausgeführt werden können. Projizieren wir dieses kleine Beispiel auf ein größeres XML-Dokument, präsentiert sich dieses im Speicher als komplexe Baumstruktur. Zur Verdeutlichung diene die folgende XML-Struktur:
    <?xml version="1.0" encoding="utf-8" ?>
    <Personen>
      <Person>
        <Name>Fischer</Name>
        <Alter>45</Alter>
        <Ort="Aachen" PLZ="52072"></Ort>
      </Person>
    </Personen>
In Abbildung 16.8 sehen Sie, wie dieses an sich noch sehr kleine XML-Fragment durch bereits eine verhältnismäßig komplexe Struktur im Speicher repräsentiert wird. Hier klicken, um das Bild zu vergrößern Abbildung 16.8 Darstellung eines XML-Dokuments als Baumstruktur ### 16.7.2 Arbeiten mit XmlDocument #### XML-Daten laden Ein XmlDocument-Objekt enthält manipulierbare XML-Daten auf der Basis von DOM. Nach der Instanziierung der Klasse stehen Ihnen mit Load und LoadXml zwei Methoden zur Verfügung, um das Objekt mit Daten zu füllen. Der Methode Load können Sie einen URL als Zeichenfolge übergeben. Der URL kann eine lokale Datei oder eine HTTP-URL (also eine Webadresse) sein. Weitere Überladungen erlauben auch die Übergabe eines Stream-, TextReader- oder XmlReader-Objekts.
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\Personen.xml");
Liegt das XML-Dokument in einer Zeichenfolge vor, bietet sich die Methode LoadXml an. Bedingung ist, dass die Zeichenfolge wohlgeformtes XML beschreibt, da ansonsten eine XmlException ausgelöst wird.
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<Person><Zuname>Franz</Zuname></Person>");
Die Variable doc beschreibt nun das sich im Speicher befindliche XML-Dokument. Mit den Eigenschaften und Methoden des XmlDocument-Objekts können Sie anschließend beliebig durch die Baumstruktur der Knoten navigieren und diese beliebig bearbeiten. ### 16.7.3 XmlDocument und XPathNavigator Es gibt noch eine andere Variante, durch die Baumstruktur zu navigieren. Der Ausgangspunkt sind dabei die Methoden und Eigenschaften der Klasse XPathNavigator. Dazu wird auf die Referenz des XmlDocuments die Methode CreateNavigator aufgerufen, deren Rückgabewert ein XPathNavigator-Objekt ist.
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\Personen.xml");
    XPathNavigator navi = doc.CreateNavigator();
Die Kombination zwischen einem XPathNavigator und einem XmlDocument bietet den Vorteil, die zahlreichen Navigationsmöglichkeiten von XPathNavigator nutzen zu können. Allerdings hat dieser Verbund einen gravierenden Nachteil, denn das XML-Dokument muss zweimal im Speicher abgebildet werden, was bei der Größe vieler XML-Dokumente aus performance- und ressourcentechnischer Sicht nicht mehr akzeptabel ist. #### Geänderte Daten speichern Einer der großen Vorteile von DOM gegenüber anderen Techniken ist, dass die XML-Daten geändert und gespeichert werden können. Dazu stellt das XmlDocument-Objekt die Methode Save bereit, der als Argument eine den Speicherort beschreibende Zeichenfolge oder ein Stream-, TextWriter- oder XmlWriter-Objekt übergeben wird. ### 16.7.4 Die Klasse »XmlNode« (Operationen mit Knoten) Ein Objekt vom Typ XmlNode repräsentiert im Speicher einen einzelnen Knoten eines XML-Dokuments. XmlNode ist eine abstrakte Klasse, die viele Methoden und Eigenschaften bereitstellt, um durch eine Knotenstruktur zu navigieren, sie zu ändern oder auch zu ergänzen. Zahlreiche Ableitungen von XmlNode beschreiben die unterschiedlichen Knotentypen innerhalb eines XML-Dokuments und erweitern die geerbten Eigenschaften und Methoden um knotenspezifische. Zu diesen Ableitungen gehören auch die Klassen, die beispielsweise ein Attribut oder die XML-Deklaration beschreiben sowie die Klasse XmlDocument. Einen Überblick über die Vererbungshierarchie erhalten Sie in Abbildung 16.9. Wenn Sie sich die Liste der Eigenschaften in der Dokumentation der Klasse XmlNode ansehen, werden Sie feststellen, dass sich die meisten Eigenschaften zwei Kategorien zuordnen lassen: Sie werden einige Eigenschaften finden, die rein der informellen Analyse eines Knotens dienen, und eine zweite Gruppe, die der Navigation dient. Hier klicken, um das Bild zu vergrößern Abbildung 16.9 Die Ableitungen der Klasse »XmlNode« #### Inhalt eines Knotens auswerten Sehen wir uns in Tabelle 16.14 zuerst die Eigenschaften an, die informellen Charakter haben.

Tabelle 16.14 XmlNode-Eigenschaften, die informelle Informationen liefern

Eigenschaft | Beschreibung
InnerText | Ruft die verketteten Werte des Knotens und sämtlicher diesem untergeordneten Knoten ab oder legt diese fest. Die zurückgegebene Zeichenfolge enthält kein XML-Markup.
InnerXml | Liefert das Markup, das nur die untergeordneten Knoten dieses Knotens darstellt, oder legt dieses fest.
IsReadOnly | Gibt an, ob der Knoten schreibgeschützt ist.
Name | Ruft den Bezeichner des Knotens ab.
NamespaceURI | Ruft den Namespace-URI dieses Knotens ab.
NodeType | Ruft den Typ des Knotens ab.
OuterXml | Ruft das Markup ab, das den aktuellen Knoten und alle ihm untergeordneten Knoten darstellt.
Prefix | Ruft das Namespace-Präfix dieses Knotens ab oder legt dieses fest.
Value | Liefert den Wert des Knotens ab oder legt ihn fest. Der zurückgegebene Wert hängt vom Knotentyp ab. Beispielsweise liefert der Typ XmlNodetype.Element null, während es sich bei einem Knoten vom Typ Text um den Inhalt des Textknotens handelt.
Ein Wort noch zu der Eigenschaft NodeType, die uns den Typ des aktuellen Knotens liefert. Diese Eigenschaft ist vom Typ der Enumeration XmlNodeType. Um einen Überblick über einige der wichtigeren Mitglieder dieser Enumeration zu erhalten, sehen Sie sich bitte Tabelle 16.15 an.

Tabelle 16.15 Member der Enumeration »XmlNodeType« (Auszug)

Konstante | Beschreibung
Element | Beschreibt ein Element (z. B. <Person>).
Attribute | Beschreibt ein Attribut.
Text | Beschreibt den Textinhalt eines Knotens.
CDATA | Beschreibt einen CDATA-Abschnitt.
EntityReference | Beschreibt eine Entität (z.B. &lt;).
Comment | Beschreibt einen Kommentar.
WhiteSpace | Beschreibt Leerraum zwischen Markup.
Die Ausgaben der Eigenschaften InnerText, InnerXml, Name und Value hängen vom Knotentyp ab. Handelt es sich dabei beispielsweise um XmlNodeType.Element oder XmlNodeType.Attribute, wird der entsprechende Bezeichner ausgegeben. Nicht jeder Knotentyp hat einen Bezeichner, der auswertbar ist. Daher liefert Name für diese Knotengruppe einen alternativen Text, z. B. #text, #comment oder #cdata-section. Andererseits haben einige Knoten keinen Inhalt, der mit Value abgerufen werden könnte. Dann ist die Rückgabe null. Nehmen wir an, im XML-Fragment
    <Person>Franz</Person>
würde die Variable node vom Typ XmlNode den Knoten Person beschreiben. (Anmerkung: Befindet sich das genannte Element in einem XML-Dokument, muss zuerst dorthin navigiert werden.) Rufen wir die Name-Eigenschaft ab, wird uns
    Person
ausgegeben. InnerXml und InnerText liefern beide gleichermaßen
    Franz
zurück, während Value keinen Inhalt hat und null ist. Navigieren wir nun weiter, sodass node auf den Textknoten zeigt, wird Name die Ausgabe
    #text
haben und Value sowie InnerText den gewünschten Inhalt Franz anzeigen. Das folgende Beispielprogramm zeigt die informellen Ausgaben im Zusammenhang. An LoadXml wird dabei eine XML-Struktur übergeben. Anschließend wird mit der Eigenschaft DocumentElement das Stammelement der Struktur abgerufen und in der Variablen root gespeichert. Der Rückgabewert der Eigenschaft DocumentElement ist vom Typ XmlElement, auf dem danach exemplarisch Eigenschaften aufgerufen werden.
    // -----------------------------------------------------
    // Beispiel: ...\Kapitel 16\XmlNodeSample
    // -----------------------------------------------------
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<Person><Name Zuname='Müller'><Alter>34</Alter>" +
                "Peter</Name></Person>");
    XmlNode root = doc.DocumentElement;
    // Eigenschaft 'Name'
    Console.WriteLine("Name:");
    Console.WriteLine("{0}\n", root.Name);
    // Eigenschaft 'OuterXml'
    Console.WriteLine("OuterXml:");
    Console.WriteLine("{0}\n", root.OuterXml);
    // Eigenschaft 'InnerXml'
    Console.WriteLine("InnerXml:");
    Console.WriteLine("{0}\n", root.InnerXml);
    // Eigenschaft 'Value'
    Console.Write("Value:");
    if (root.Value == null)
    Console.WriteLine("  <leer>\n");
    else
    Console.WriteLine("\n{0}\n",root.Value);
    // Eigenschaft 'InnerText'
    Console.WriteLine("InnerText:");
    Console.WriteLine("{0}",root.InnerText);
Die Ausgabe an der Konsole können Sie in Abbildung 16.10 sehen. Hier klicken, um das Bild zu vergrößern Abbildung 16.10 Die Ausgabe des Beispiels »XmlNodeSample« #### Mit den Eigenschaften eines XmlNode-Objekts navigieren Jetzt wollen wir uns die Eigenschaften eines XmlNode-Objekts anschauen, die der Navigation in einer XML-Struktur dienen. Auch hier sollten Sie sich zunächst einen Überblick über die Eigenschaften anhand einer Tabelle verschaffen.

Tabelle 16.16 Eigenschaften des »XmlNode«-Objekts zur Navigation

Eigenschaft | Beschreibung
Attributes | Diese Eigenschaft ruft eine Auflistung vom Typ XmlAttributCollection ab, die die Attribute des aktuellen Knotens enthält.
ChildNodes | Ruft alle untergeordneten Knoten des Knotens ab.
FirstChild | Ruft das erste untergeordnete Element des aktuellen Knotens ab.
HasChildNodes | Ruft einen Wert ab, der angibt, ob dieser Knoten über untergeordnete Knoten verfügt.
Item | Ruft das erste untergeordnete Element mit dem angegebenen Bezeichner ab.
LastChild | Ruft das letzte untergeordnete Element des aktuellen Knotens ab.
NextSibling | Ruft den nächsten nebengeordneten Knoten ab, der dem aktuellen Knoten folgt.
ParentNode | Ruft das übergeordnete Element des aktuellen Knotens ab.
PreviousSibling | Ruft den vorhergehenden nebengeordneten Knoten des aktuellen Knotens ab.
Die Vorgehensweise beim Einsatz dieser Eigenschaften ähnelt der Navigation mit XmlReader oder XPathNavigator. Von jedem beliebigen XmlNode-Objekt aus können Sie mit den Eigenschaften FirstChild, LastChild, NextSibling, PreviousSibling und ParentNode zum nächsten untergeordneten Element, zum letzten untergeordneten Element, zum nächsten bzw. letzten nebengeordneten Element oder gar zum übergeordneten Element wechseln. #### Die Collection »XmlChildNodes« Die Eigenschaft ChildNodes liefert eine Liste aller direkt untergeordneten Knoten ab. Diese wird durch den Typ XmlNodeList beschrieben. Viele Möglichkeiten bietet diese Liste nicht, aber Sie können diese Liste in einer Schleife durchlaufen, sich mit Count die Anzahl der untergeordneten Knoten besorgen oder mit Item einen bestimmten Knoten an der angegebenen Indexposition in der Liste abfragen.
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\Personen.xml");
    XmlNode root = doc.DocumentElement;
    XmlNodeList nodeList = root.ChildNodes;
    foreach (XmlNode node in nodeList)
    {
      ...
    }
Die Schleifenvariable node kann natürlich dazu benutzt werden, um den damit aktuell referenzierten Knoten weiter zu untersuchen, ob er seinerseits selbst untergeordnete Knoten hat. Um einen bestimmten untergeordneten Knoten auszuwerten, können Sie sich die Referenz auf das diesen Knoten beschreibende XmlNode-Objekt mit
    XmlNode node = irgendeinKnoten.ChildNodes[2];
oder alternativ auch mit
    XmlNode node = nodeList[2];
besorgen. In beiden Fällen erhalten Sie die Referenz auf den dritten untergeordneten Knoten. #### Auswerten eines kompletten XML-Dokuments Um ein XML-Dokument komplett auszuwerten, bietet sich ein rekursiver Aufruf einer Methode an, in der eine Untersuchung des aktuellen Knotens stattfindet. Hier spielen die beiden Eigenschaften Attributes und HasChildNodes ihre ganze Stärke aus.
    // -----------------------------------------------------
    // Beispiel: ...\Kapitel 16\XmlNodeNavigation
    // -----------------------------------------------------
    static void Main(string[] args) {
      XmlDocument doc = new XmlDocument();
      doc.Load(@"..\..\Personen.xml");
      XmlNode root = doc.DocumentElement;
      GetNodes(root, 0);
      Console.ReadLine();
    }
    static void GetNodes(XmlNode node, int level) {
      switch (node.NodeType) {
        // prüfen, ob es sich um ein Element handelt
        case XmlNodeType.Element:
          Console.Write(new string(' ', level * 2));
          Console.Write("<{0}", node.Name);
          // prüfen, ob das aktuelle Element Attribute hat
          if (node.Attributes != null) {
            foreach (XmlAttribute attr in node.Attributes)
              Console.Write(" {0}='{1}'", attr.Name, attr.Value);
          }
          Console.Write(">");
          // prüfen, ob das aktuelle Element untergeordnete
          // Elemente hat
          if (node.HasChildNodes)
            foreach (XmlNode child in node.ChildNodes) {
              if (child.NodeType != XmlNodeType.Text)
                Console.WriteLine();
              GetNodes(child, level + 1);
            }
          break;
        // prüfen, ob es sich um auswertbare Daten handelt
        case XmlNodeType.Text:
          Console.Write(node.Value);
          break;
      }
    }
Liefert Attributes einen Wert ungleich null, hat das aktuelle Element Attribute, die in einer Schleife abgefragt werden können. HasChildNodes liefert einen Boolean. Ist dieser true, kann die Liste der untergeordneten Knoten mit der Eigenschaft ChildNodes durchlaufen werden. Dabei kommt es zu einem rekursiven Aufruf der benutzerdefinierten Methode GetNodes. In diesem Beispiel wurden keine Namespaces und Präfixe zur eindeutigen Identifizierung der Elemente in der XML-Datei benutzt. Bei der Navigation spielt das keine Rolle, da eventuell vorhandene Namespaces als Attribute bewertet werden und die Elemente samt dem Präfix ausgegeben werden. Der Parameter level der Methode ChildNodes dient dazu, optisch ansprechende Einzüge in der Ausgabe darstellen zu können. Hier klicken, um das Bild zu vergrößern Abbildung 16.11 Ausgabe des Beispiels »XmlNodeNavigation« #### XPath-Ausdrücke verwenden Um ein Element in einer XML-Struktur eindeutig zu adressieren, können Sie auch einen XPath-Ausdruck festlegen. Die Klasse XmlNode kennt zwei Methoden, denen Sie einen XPath-Ausdruck übergeben können: SelectNodes und SelectSingleNode. Beide Methoden werden auch von der Klasse XmlDocument unterstützt und können daher auch auf der Referenz des XmlDocument-Objekts aufgerufen werden. SelectNodes liefert eine Auflistung des Typs XmlNodeList mit allen gefundenen Elementen zurück, die dem XPath-Ausdruck entsprechen. Das folgende Codefragment gibt die Liste aller Zunamen aus der XML-Datei Personen.xml aus.
    XmlDocument doc = new XmlDocument();
    doc.Load(@"..\..\Personen.xml");
    XmlNodeList liste = doc.SelectNodes("//Person//Zuname");
    foreach(XmlNode temp in liste)
      Console.WriteLine("Name: {0}", temp.InnerXml);
Sind Sie nur am ersten Element interessiert, liefert der Aufruf von SelectSingleNode genau dieses.
    XmlNodeList liste = root.SelectNodes("//Person//Zuname");
Beide Methoden haben zudem noch jeweils eine Überladung, die ein Objekt vom Typ XmlNamespaceManager erwartet, mit dem Sie die Präfixe der Elemente auflösen können. Diese Klasse wurde bereits in Abschnitt 16.6.6 behandelt. #### Suche nach bestimmten Elementen Im folgenden Beispielprogramm wird demonstriert, wie die Inhalte bestimmter Elemente ausgewertet werden können. Im Mittelpunkt steht dabei die Methode GetElementsByTagName, der das zu durchsuchende Element angegeben wird.
    // -----------------------------------------------------
    // Beispiel: ...\Kapitel 16\SearchForElements
    // -----------------------------------------------------
    static void Main(string[] args){
      XmlDocument doc = new XmlDocument();
      doc.Load(@"..\..\Personen.xml");
      XmlElement root = doc.DocumentElement;
      XmlNodeList elemList = root.GetElementsByTagName("Zuname");
      // Resultate anzeigen
      for (int i = 0; i < elemList.Count; i++) {
        Console.WriteLine(elemList[i].InnerXml);
      }
      Console.ReadLine();
    }
Als Ergebnis werden die Namen aller Im XML-Dokument enthaltenen Personen ausgegeben. ### 16.7.5 XML-Struktur manipulieren Zahlreiche Methoden sind in den Klassen XmlDocument, XmlNode und XmlNodeList definiert, um neue Knoten hinzuzufügen, vorhandene zu ändern oder gar zu löschen. Um einen besseren Überblick über die sich bietenden Möglichkeiten zu bekommen, wird den drei Operationen des Hinzufügens, der Editierung und des Löschens jeweils ein eigener Abschnitt gewidmet. #### Knoten hinzufügen Um zu zeigen, wie zusätzliche Knoten einer XML-Struktur hinzugefügt werden, soll im nächsten Codefragment zunächst eine komplett neue XML-Struktur bereitgestellt werden, die wie folgt aussieht:
    <?xml version="1.0" encoding="ibm850" ?>
    <!--Dies ist ein Kommentar-->
    <Personen>
      <Person>Peter</Person>
    </Personen>
Code, der dies leistet, würde wie folgt aussehen:
    XmlNode nodePerson, nodeName;
    XmlDocument doc = new XmlDocument();
    XmlComment cmt = doc.CreateComment("Dies ist ein Kommentar");
    doc.AppendChild(cmt);
    XmlNode nodeRoot = doc.CreateElement("Personen");
    doc.AppendChild(nodeRoot);
    nodePerson = doc.CreateElement("Person");
    nodeRoot.AppendChild(nodePerson);
    nodeName = doc.CreateTextNode("Peter");
    nodePerson.AppendChild(nodeName);
    doc.Save(Console.Out);
Die resultierende XML-Struktur weist insgesamt vier Knoten auf, die der Reihe nach erzeugt werden. Dazu stellt die Klasse XmlDocument mehrere passende CreateXxx-Methoden bereit. Im Code werden mit CreateComment, CreateElement und CreateTextNode ein Kommentar, zwei Elementknoten und ein Textknoten erzeugt. Die Basisklasse aller Knoten, XmlNode, vererbt an alle Ableitungen, also auch an XmlDocument, die Methode AppendChild. Der Aufruf dieser Methode auf einen bestimmten Knoten bewirkt, dass die dem Parameter übergebene XmlNode-Referenz zu einem untergeordneten Knoten des Knotens wird, auf dem die Methode aufgerufen wird. Nach allem bleibt festzuhalten, dass immer zwei Schritte erforderlich sind, um einen neuen Knoten einem existierenden XML-Dokument hinzuzufügen:
1. | Erzeugen Sie den neuen Knoten mit einer Create-Methode auf die Referenz des XmlDocument-Objekts.
2. | Rufen Sie die Methode AppendChild auf dem Knoten auf, dem der neue Knoten hinzugefügt werden soll. Die Referenz des neuen Knotens wird dabei als Argument dem Methodenaufruf übergeben.
Wird ein neues XML-Dokument erzeugt, dürfen Sie auf dessen Referenz nur einmal die CreateElement-Methode aufrufen, da das so hinzugefügte Element das Wurzelelement des XML-Dokuments beschreibt, von dem es bekanntlich zur Sicherstellung der Wohlgeformtheit nur eines geben darf. In Tabelle 16.17 finden Sie eine Übersicht der gängigsten CreateXxx-Methoden von XmlDocument.

Tabelle 16.17 Die »Create«-Methoden der Klasse »XmlDocument« (Auszug)

Methode | Beschreibung
CreateAttribute | Erstellt ein neues Objekt vom Typ XmlAttribute.
CreateCDataSection | Erstellt ein neues Objekt vom Typ XmlCDataSection.
CreateComment | Erstellt ein neues Objekt vom Typ XmlComment.
CreateDefaultAttribute | Erstellt ein Standardattribut mit dem angegebenen Präfix, lokalen Namen und Namespace-URI.
CreateElement | Erstellt ein neues Objekt vom Typ XmlElement.
CreateNode | Erstellt ein neues Objekt vom Typ XmlNode.
CreateProcessingInstruction | Erstellt ein neues Objekt vom Typ XmlProcessingInstruction.
CreateTextNode | Erstellt ein neues Objekt vom Typ XmlText.
CreateXmlDeclaration | Erstellt ein neues Objekt vom Typ XmlDeclaration.
### 16.7.6 Knoten ändern Um einen Knoten zu ändern, benötigen Sie zuerst die Referenz auf den entsprechenden Knoten. Dabei kommen die diversen Navigationsmethoden infrage oder, was deutlich besser ist, ein XPath-Ausdruck. Das zeigt das folgende Beispielprogramm, das zum Ziel hat, in der Datei Personen.xml das Alter von Klaus Meier neu festzulegen.
    // -----------------------------------------------------
    // Beispiel: ...\Kapitel 16\EditXmlNode
    // -----------------------------------------------------
    static void Main(string[] args) {
      XmlDocument doc = new XmlDocument();
      doc.Load(@"..\..\Personen.xml");
      XmlNode root = doc.DocumentElement;
      // Referenz auf das zu ändernde Element besorgen
      XmlNode node =
       root.SelectSingleNode("//Person[Zuname ='Meier']/Alter");
      if (node != null) {
        XmlNode nodeAlter = node.FirstChild;
        // Alter ändern
        nodeAlter.Value = "33";
        XmlNode parent = node.ParentNode;
        GetNodes(parent, 0);
        Console.ReadLine();
        return;
      else {
        Console.WriteLine("Der angegebene Zuname existiert nicht.");
        Console.ReadLine();
      }
    }
    static void GetNodes(XmlNode node, int level) {
      switch (node.NodeType) {
        // prüfen, ob es sich um ein Element handelt
        case XmlNodeType.Element:
          Console.Write(new string(' ', level * 2));
          Console.Write("<{0}>", node.Name);
          if (node.HasChildNodes)
            foreach (XmlNode child in node.ChildNodes) {
              if (child.NodeType != XmlNodeType.Text)
                Console.WriteLine();
              GetNodes(child, level + 1);
            }
          break;
        // prüfen, ob es sich um auswertbare Daten handelt
        case XmlNodeType.Text:
          Console.Write(node.Value);
          break;
      }
    }
Nach dem Laden der Datei und der Referenzierung des Wurzelelements wird mit SelectSingleNode ein XPath-Ausdruck abgesetzt, der anhand des Elements Zuname nach Meier sucht:
     //Person[Zuname ='Meier']/Alter
Man sollte berücksichtigen, dass die entsprechende Person nicht vom XML-Dokument beschrieben wird. Der Rückgabewert der Methode SelectSingleNode ist in dem Fall null. Wird das Element Meier gefunden, liefert das Ergebnis des XPath-Ausdrucks das Element Alter und wird mit FirstChild auf den Textknoten verschoben, dessen Inhalt anschließend über die Eigenschaft Value verändert wird. Alternativ hätten Sie auch direkt die Referenz auf das Element Alter benutzen können, um die Änderung vorzunehmen. Sie müssen dann die Eigenschaft InnerText ändern:
    XmlNode node =
      root.SelectSingleNode("//Person[Zuname ='Meier']/Alter");
    if (node != null) {
      node.InnerText = "33";
      ...
Um uns vom Erfolg zu überzeugen, wird am Ende das die Person Meier betreffende Element komplett ausgegeben. Hierzu dient die Methode GetNodes, die ich Ihnen in ähnlicher Form bereits in Abschnitt 16.7.4 im Programm XmlNodeNavigation vorgestellt habe. #### Attribute ändern Um einem Element ein Attribut hinzuzufügen, muss auf der Referenz des entsprechenden Elements die Methode SetAttribute aufgerufen werden. Der Aufruf von SetAttribute erzeugt aber nur dann ein neues Attribut, wenn es ein solches namentlich noch nicht gibt. Ansonsten wird der Inhalt des gefundenen Attributs nur geändert. Im folgenden Beispielcode wird sowohl ein neues Attribut zu einem XmlElement hinzugefügt als auch ein vorhandenes editiert.
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<Personen><Person><Name>Fischer</Name>" +
        "<Daten alter='56' ort='Bonn'/></Person></Personen>");
    XmlNode root = doc.DocumentElement;
    string xpath = "/Personen/Person/Daten";
    XmlElement node =   (XmlElement)root.SelectSingleNode(xpath);
    // Attribut 'ort' ändern
    node.SetAttribute("ort", "Aachen");
    // Attribut 'plz' hinzufügen
    node.SetAttribute("plz", "52072");
    // Ausgabe der XML-Struktur
    Console.WriteLine(doc.InnerXml);
### 16.7.7 Löschen in einem XML-Dokument #### Element löschen Um ein Element aus einer XML-Struktur zu entfernen, rufen Sie die Methode RemoveChild auf dem übergeordneten Element des zu löschenden Elements auf. Der Methode wird die Referenz auf das Element übergeben, das entfernt werden soll. Im folgenden Codefragment wird aus der Datei die dritte Person (Petra Schmidt) gelöscht.
    XmlDocument doc = new XmlDocument();
    doc.Load("Personen.xml");
    XmlNode root = doc.DocumentElement;
    XmlNode node = root.SelectSingleNode("//Person[Zuname='Schmidt']");
    root.RemoveChild(node);
#### Attribute löschen Zum Entfernen eines Attributs stellt Ihnen die Klasse XmlElement vier Methoden zur Verfügung, die Sie Tabelle 16.18 entnehmen können.

Tabelle 16.18 Methoden zum Löschen von Attributen

Methode | Beschreibung
RemoveAllAttributes | Löscht alle angegebenen Attribute des Elements.
RemoveAttribute | Entfernt das angegebene Attribut, dessen Bezeichner der Methode übergeben wird.
RemoveAttributeAt | Entfernt den Attributknoten mit dem angegebenen Index aus dem Element.
RemoveAttributeNode | Löscht das Attribut, das als XmlAttribute-Referenz angegeben wird.
Im nächsten Codebeispiel wird exemplarisch das Attribut Ort der Person Meier gelöscht. Dabei wird die Methode RemoveAttribute verwendet. Zum Auffinden dient ein XPath-Ausdruck, dessen Rückgabe bekanntlich vom Typ XmlNode ist. Da die Löschmethode jedoch in der Klasse XmlElement definiert ist, muss das Resultat der Methode SelectSingleNode entsprechend typumgewandelt werden.
    string xpath = "//Person[Zuname='Meier']/Adresse";
    XmlDocument doc = new XmlDocument();
    doc.Load(@"..\..\Personen.xml");
    XmlNode root = doc.DocumentElement;
    XmlElement element = (XmlElement)root.SelectSingleNode(xpath);
    Console.WriteLine(element.Name);
    element.RemoveAttribute("Ort");
#### Untergeordnete Elemente und Attribute löschen Mit RemoveAll stellen die Klassen XmlNode und XmlElement eine Methode bereit, die alle angegebenen Attribute und untergeordneten Elemente des aktuellen Knotens entfernt.
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<Person ort='Aachen' plz='52072'>" +
                "<Name>Franz Schmitz</Name></Person>");
    XmlNode root = doc.DocumentElement;
    root.RemoveAll();
Als Resultat bleibt als Ergebnis nur noch
    <Person></Person>