XML-Strukturänderung im Arbeitsspeicher kontra funktionaler Konstruktion (LINQ to XML)

Zur Änderung der Form eines XML-Dokuments wird üblicherweise die XML-Struktur an Ort und Stelle geändert. Eine typische Anwendung lädt ein Dokument in einen Datenspeicher, z. B. DOM oder LINQ to XML, verwendet eine Programmierschnittstelle, um Knoten einzufügen oder zu löschen oder ihren Inhalt zu ändern, und speichert den XML-Code dann in einer Datei bzw. überträgt ihn über ein Netzwerk.

LINQ to XML ermöglicht eine andere Herangehensweise, die sich in vielen Szenarios als hilfreich erweist: die funktionale Konstruktion. Bei der funktionalen Konstruktion wird das Ändern von Daten als ein Transformationsproblem und nicht als detaillierte Manipulation eines Datenspeichers angesehen. Wenn Sie eine Datendarstellung effizient von einer Form in eine andere Form transformieren können, sieht das Ergebnis genauso aus, als hätten Sie einen Datenspeicher so manipuliert, dass er eine andere Form erhalten hat. Ein entscheidender Punkt bei der funktionalen Konstruktion ist die Übergabe der Ergebnisse von Abfragen an die Konstruktoren XDocument und XElement.

Das Schreiben des Transformationscodes kostet Sie häufig nur einen Bruchteil der Zeit, die Sie für die Manipulation des Datenspeichers hätten aufwenden müssen, und der entstehende Code ist robuster und leichter zu unterhalten. In diesen Fällen ist die Verwendung von Transformationscode die effektivere Möglichkeit, Daten zu ändern, auch wenn die Transformation mehr CPU-Leistung beansprucht. Wenn Entwickler*innen mit dem funktionalen Ansatz vertraut sind, ist der resultierende Code in vielen Fällen verständlicher, und es ist einfach, den Code zu finden, der jeden Teil der Struktur ändert.

Das Ändern einer XML-Struktur an Ort und Stelle ist vielen DOM-Programmierer*innen vertrauter, während Code, der mit dem funktionalen Ansatz geschrieben wurde, für Entwickler*innen, die diesen Ansatz nicht kennen, eher fremd aussieht. Wenn an einer großen XML-Struktur nur eine kleine Änderung vorgenommen werden muss, wird die CPU durch das Ändern der Struktur an Ort und Stelle weniger beansprucht.

Dieser Artikel enthält Beispiele für beide Ansätze. Angenommen, Sie möchten das folgende einfache XML-Dokument so ändern, dass aus den Attributen Elemente werden.

<?xml version="1.0" encoding="utf-8" ?>
<Root Data1="123" Data2="456">
  <Child1>Content</Child1>
</Root>

Die ersten der folgenden Beispiele verwenden den herkömmlichen direkten Änderungsansatz und der zweite verwendet den funktionalen Konstruktionsansatz.

Beispiel: Transformieren von Attributen in Elemente mit dem herkömmlichen direkten Ansatz

Sie können prozeduralen Code zum Erstellen von Elementen aus Attributen schreiben und dann die Attribute löschen. Dies sieht wie folgt aus:

XElement root = XElement.Load("Data.xml");
foreach (XAttribute att in root.Attributes()) {
    root.Add(new XElement(att.Name, (string)att));
}
root.Attributes().Remove();
Console.WriteLine(root);
Dim root As XElement = XElement.Load("Data.xml")
For Each att As XAttribute In root.Attributes()
    root.Add(New XElement(att.Name, att.Value))
Next
root.Attributes().Remove()
Console.WriteLine(root)

Dieses Beispiel erzeugt die folgende Ausgabe:

<Root>
  <Child1>Content</Child1>
  <Data1>123</Data1>
  <Data2>456</Data2>
</Root>

Beispiel: Transformieren von Attributen in Elemente mit dem funktionalen Konstruktionsansatz

Bei der funktionalen Konstruktion wird zunächst eine neue Struktur erstellt. Dann werden Elemente und Attribute aus der Quellstruktur ausgewählt und der neuen Struktur hinzugefügt, wobei sie entsprechend transformiert werden.

XElement root = XElement.Load("Data.xml");
XElement newTree = new XElement("Root",
    root.Element("Child1"),
    from att in root.Attributes()
    select new XElement(att.Name, (string)att)
);
Console.WriteLine(newTree);
Dim root As XElement = XElement.Load("Data.xml")
Dim newTree As XElement = _
    <Root>
        <%= root.<Child1> %>
        <%= From att In root.Attributes() _
            Select New XElement(att.Name, att.Value) %>
    </Root>
Console.WriteLine(newTree)

Dieses Beispiel gibt denselben XML-Code wie das erste Beispiel aus. Beachten Sie dabei aber, dass Sie beim funktionalen Ansatz die entstehende Struktur des neuen XML-Codes auch tatsächlich sehen können. Sie können die Erstellung des Root-Elements, den Code, der das Child1-Element aus der ursprünglichen Struktur abruft, und den Code sehen, der die Attribute aus der Quellstruktur in Elemente in der neuen Struktur transformiert.

Das funktionale Beispiel in diesem Fall ist weder kürzer noch einfacher als das erste Beispiel. Wenn an einer XML-Struktur aber viele Änderungen vorgenommen werden müssen, wird der prozedurale Ansatz ziemlich komplex und in gewisser Weise stumpf. Im Unterschied dazu erstellen Sie beim funktionalen Ansatz lediglich den gewünschten XML-Code mit entsprechenden eingebetteten Abfragen und Ausdrücken, sodass Sie den gewünschten Inhalt erhalten. Der funktionale Ansatz erzeugt Code, der leichter zu unterhalten ist.

Beachten Sie, dass der funktionale Ansatz in diesem Fall wahrscheinlich nicht so gut funktioniert wie die direkte Bearbeitung der Struktur. Das Hauptproblem dabei ist, dass beim funktionalen Ansatz kurzlebigere Objekte entstehen. Der funktionale Ansatz zahlt sich aber aus, wenn der Programmierer dadurch produktiver wird.

Das hier verwendete Beispiel ist ein sehr einfaches Beispiel, das lediglich dazu dient, die grundlegenden Unterschiede zwischen den beiden Ansätzen zu verdeutlichen. Beim Transformieren großer XML-Dokumente sind beim funktionalen Ansatz eindeutig größere Produktivitätsgewinne zu erzielen.