Wie mache ich eine tiefe Kopie eines Elements in LINQ to XML?

75

Ich möchte eine tiefe Kopie eines LINQ to XML XElement erstellen. Der Grund, warum ich dies tun möchte, ist, dass das Dokument einige Knoten enthält, von denen ich geänderte Kopien erstellen möchte (im selben Dokument). Ich sehe keine Methode, um dies zu tun.

Ich könnte das Element in eine XML-Zeichenfolge konvertieren und dann erneut analysieren, aber ich frage mich, ob es einen besseren Weg gibt.

Daniel Plaisted
quelle
Daniel - Ich denke, Sie sollten die akzeptierte Antwort auf diese Frage überdenken.
Jim G.
Fertig - Ich habe die akzeptierte Antwort geändert.
Daniel Plaisted

Antworten:

137

Es besteht keine Notwendigkeit zu reparieren. Einer der Konstruktoren von XElement nimmt ein anderes XElement und erstellt eine tiefe Kopie davon:

XElement original = new XElement("original");
XElement deepCopy = new XElement(original);

Hier sind einige Unit-Tests, um zu demonstrieren:

[TestMethod]
public void XElementShallowCopyShouldOnlyCopyReference()
{
    XElement original = new XElement("original");
    XElement shallowCopy = original;
    shallowCopy.Name = "copy";
    Assert.AreEqual("copy", original.Name);
}

[TestMethod]
public void ShouldGetXElementDeepCopyUsingConstructorArgument()
{
    XElement original = new XElement("original");
    XElement deepCopy = new XElement(original);
    deepCopy.Name = "copy";
    Assert.AreEqual("original", original.Name);
    Assert.AreEqual("copy", deepCopy.Name);
}
Jonathan Moffatt
quelle
6
Ich glaube , dass der Konstruktor in diesem Beispiel verwendete allgemein als bekannt ist Copykonstruktor . Hier ist das Dokument für den XElementKopierkonstruktor . Es gibt auch einen XDocumentKopierkonstruktor . Zum Glück macht LINQ to XML dies einfach ... Im Allgemeinen kann Deep Copying (oder Klonen) oft nicht so einfach sein.
DavidRR
1
Ein bisschen spät zur Party, aber der erste Testfall hat nichts mit flachem Kopieren zu tun. Dies ist überhaupt keine Kopie, sondern eine zweite Referenz auf dasselbe Objekt. Sie können dasselbe Prinzip auf alle nicht-primitiven Typen in C # anwenden.
UweB
In den Dokumenten wird genau dies gezeigt und erläutert: "Im folgenden Beispiel wird ein XML-Baum erstellt, ein Klon des Baums erstellt und anschließend DeepEquals aufgerufen, um zu testen, ob die beiden XML-Bäume gleich sind." . Im Beispiel wird ein Knoten hinzugefügt, nachdem der Kopierkonstruktor aufgerufen wurde, und DeepEqualsdamit wird gezeigt, dass die beiden Knoten jetzt unterschiedlich sind.
Abel
9

Es sieht so aus, als ob die ToString- und Resese-Methode der beste Weg ist. Hier ist der Code:

XElement copy = XElement.Parse(original.ToString());
Daniel Plaisted
quelle
dann DstNode.Add (Kopie), um es hinzuzufügen. Erledigt.
Fraga
4

Direkt von C # 3.0 auf den Punkt gebracht :

Wenn einem Element ein Knoten oder Attribut hinzugefügt wird (ob über eine Funktionskonstruktion oder eine Add-Methode), wird die Parent-Eigenschaft des Knotens oder Attributs auf dieses Element festgelegt. Ein Knoten kann nur ein übergeordnetes Element haben: Wenn Sie einem zweiten übergeordneten Knoten einen bereits übergeordneten Knoten hinzufügen, wird der Knoten automatisch tief geklont. Im folgenden Beispiel hat jeder Kunde eine separate Kopie der Adresse:

var address = new XElement ("address",
                  new XElement ("street", "Lawley St"),
                  new XElement ("town", "North Beach")
              );
var customer1 = new XElement ("customer1", address);
var customer2 = new XElement ("customer2", address);

customer1.Element ("address").Element ("street").Value = "Another St";
Console.WriteLine (
  customer2.Element ("address").Element ("street").Value);   // Lawley St

Diese automatische Vervielfältigung hält die Instanziierung von X-DOM-Objekten frei von Nebenwirkungen - ein weiteres Kennzeichen der funktionalen Programmierung.

Wonko
quelle
Dies war zu dieser Zeit eine sehr scharfe Beobachtung, und es ist tatsächlich genau das , was passiert (siehe Beispielcode dort).
Abel
-1

Das sollte funktionieren:

var copy = new XElement(original.Name, original.Attributes(),
                        original.Elements() );
Chris Cavanagh
quelle
Nein, tut es nicht. Auf diese Weise vergessen Sie, Kommentare, Textknoten, Verarbeitungsanweisungen, geerbte Namespace-Knoten, Anmerkungen, Basis-URL usw. zu kopieren new XElement(original). Eine bessere Möglichkeit ist die Verwendung des Kopierkonstruktors .
Abel
-3

Ich glaube nicht, dass es einen Mechanismus gibt, mit dem Sie eine tiefe Kopie eines XNode-Baums erstellen können. Ich denke, Sie haben zwei Möglichkeiten.

  1. Machen Sie, was Sie vorgeschlagen haben, eine Konvertierung in eine Zeichenfolge und dann zurück in einen Baum
  2. Schreiben Sie mit einem Besuchermuster auf sich

Das Besuchermuster ist sicherlich möglich, aber es wird viel Arbeit und Tests erfordern. Ich denke, Ihre beste Option ist # 1.

JaredPar
quelle