84

I want to make a deep copy of a LINQ to XML XElement. The reason I want to do this is there are some nodes in the document that I want to create modified copies of (in the same document). I don't see a method to do this.

I could convert the element to an XML string and then reparse it, but I'm wondering if there's a better way.

Jim G.
  • 15,141
  • 22
  • 103
  • 166
Daniel Plaisted
  • 16,674
  • 4
  • 44
  • 56

5 Answers5

158

There is no need to reparse. One of the constructors of XElement takes another XElement and makes a deep copy of it:

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

Here are a couple of unit tests to demonstrate:

[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);
}
TheCloudlessSky
  • 18,608
  • 15
  • 75
  • 116
Jonathan Moffatt
  • 13,309
  • 8
  • 51
  • 49
  • 6
    I believe that the constructor used in this example is generically known as a **Copy Constructor**. Here is the doc for the [`XElement` Copy Constructor](http://msdn.microsoft.com/en-us/library/bb297950%28v=vs.110%29.aspx). There is also an [`XDocument` Copy Constructor](http://msdn.microsoft.com/en-us/library/bb341301%28v=vs.110%29.aspx). Fortunately for us, **LINQ to XML** makes this easy...in general, [Deep Copying (or Cloning)](http://stackoverflow.com/questions/78536/deep-cloning-objects-in-c-sharp) can many times not be so straightforward. – DavidRR Jan 30 '14 at 21:22
  • 3
    A bit late to the party, but the first test case has nothing to do with shallow copying. This is not a copy at all, it is second reference to the same object. You can apply the same principle to all non-primitive types in C#. – UweB Nov 19 '19 at 13:36
  • The [docs show exactly this](https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xelement.-ctor?view=netframework-4.8#System_Xml_Linq_XElement__ctor_System_Xml_Linq_XElement_), and explain _"The following example creates an XML tree, creates a clone of the tree, and then calls DeepEquals, which tests whether the two XML trees are equal."_. In the example, a node is added after the copy-constructor is called, and with `DeepEquals` it is shown that the two nodes are now different. – Abel Mar 15 '20 at 23:39
10

It looks like the ToString and reparse method is the best way. Here is the code:

XElement copy = XElement.Parse(original.ToString());
Daniel Plaisted
  • 16,674
  • 4
  • 44
  • 56
5

Lifted directly from C# 3.0 in a Nutshell:

When a node or attribute is added to an element (whether via functional construction or an Add method) the node or attribute's Parent property is set to that element. A node can have only one parent element: if you add an already parented node to a second parent, the node is automatically deep-cloned. In the following example, each customer has a separate copy of address:

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

This automatic duplication keeps X-DOM object instantiation free of side effects—another hallmark of functional programming.

Wonko
  • 2,171
  • 2
  • 17
  • 10
  • This was a very keen observation at the time, and it is indeed exactly [what happens](https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xelement.-ctor?view=netframework-4.8#System_Xml_Linq_XElement__ctor_System_Xml_Linq_XElement_) (see example code there). – Abel Mar 15 '20 at 23:40
-1

This should work:

var copy = new XElement(original.Name, original.Attributes(),
                        original.Elements() );
pb2q
  • 58,613
  • 19
  • 146
  • 147
  • No it doesn't. This way you forget to copy comments, text nodes, processing instructions, inherited namespace nodes, annotations, base-uri etc. A better way is to use the copy-constructor `new XElement(original)`. – Abel Mar 15 '20 at 23:43
-3

I do not believe there is an existing mechanism that allows you to perform a deep copy of an XNode style tree. I think you are left with two options.

  1. Do as you suggested an convert to a string and then back into a tree
  2. Write on yourself with a visitor pattern

The visitor pattern is certainly possible but it will take a good deal of work an testing. I think your best option is #1.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454