如何在LINQ to XML中进行深度复制元素?

85

我想要制作一个LINQ to XML XElement的深层副本。 我之所以想这样做是因为文档中有一些节点,我想创建修改后的副本(在同一文档中)。 我没有看到可以执行此操作的方法。

我可以将元素转换为XML字符串,然后重新解析它,但我想知道是否有更好的方法。


丹尼尔 - 我认为你应该重新考虑接受的答案。 - Jim G.
5个回答

159

不需要重新解析。XElement的其中一个构造函数接受另一个XElement并对其进行深层复制:

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

以下是几个单元测试的示例:

[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);
}

6
我认为,在这个例子中使用的构造函数通常被称为复制构造函数。这是XElement复制构造函数的文档。还有一个XDocument复制构造函数。幸运的是,LINQ to XML使这变得容易......总的来说,深度复制(或克隆)有时可能并不那么简单。 - DavidRR
3
虽然有点晚了,但是第一个测试案例与浅拷贝没有任何关系。这完全不是一种拷贝,而是对同一对象的第二个引用。这个原则同样适用于 C# 中的所有非基元类型。请注意保持原文意思,并用通俗易懂的语言表述。 - UweB
文档显示了这一点,并解释了_"以下示例创建一个XML树,创建该树的克隆,然后调用DeepEquals,测试这两个XML树是否相等。"_ 在示例中,在调用复制构造函数之后添加了一个节点,并使用DeepEquals表明这两个节点现在不同。 - Abel

10

看起来 ToString 和 reparse 方法是最好的方式。这里是代码:

XElement copy = XElement.Parse(original.ToString());

然后使用 DstNode.Add(copy) 将其添加。完成。 - Fraga

5

直接摘自C# 3.0 in a Nutshell

当节点或属性被添加到元素中(无论是通过函数构造还是Add方法),该节点或属性的Parent属性将设置为该元素。一个节点只能有一个父元素:如果您将已经存在父元素的节点添加到第二个父元素中,则节点会自动进行深层克隆。在下面的示例中,每个客户都有一个单独的地址副本:

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

这种自动复制使得 X-DOM 对象的实例化不受副作用影响,这也是函数式编程的又一特点。

当时这是一个非常敏锐的观察,确实就是 发生的事情 (请参阅示例代码)。 - Abel

-1

这应该可以工作:

var copy = new XElement(original.Name, original.Attributes(),
                        original.Elements() );

不,它并不会。这种方式会忘记复制注释、文本节点、处理指令、继承的命名空间节点、注解、基础URI等。更好的方法是使用复制构造函数 new XElement(original) - Abel

-3

我不相信有现成的机制可以让你执行XNode样式树的深度复制。我认为你只剩下两个选择。

  1. 像你建议的那样,将其转换为字符串,然后再转回树形结构
  2. 使用访问者模式自己编写

访问者模式当然是可行的,但需要大量的工作和测试。我认为你最好选择第一种方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接