【问题标题】:How do I merge two XDocuments removing duplicates如何合并两个 XDocument 删除重复项
【发布时间】:2013-09-26 02:56:30
【问题描述】:

我有两个 XML 文件(*.resx 文件),我试图将它们合并到一个删除重复文件中,但我无法这样做。我尝试了以下方法但没有成功:

            var resource1 = XDocument.Load("C:\\Resources.resx");

            var resource2 = XDocument.Load("C:\\Resources2.resx");

           // This results in a file with all the nodes from the second file included inside 
          // the root element of the first file to form a properly formatted, concatenated file.
            resource1.Descendants().FirstOrDefault().Add(resource2.Descendants().FirstOrDefault().Nodes());

            var nodeContent = new List<string>();               

            foreach (XElement node in resource1.Root.Elements())
            {                    

                if (nodeContent.Contains(node.ToString()))
                    resource1.Remove();
                else
                    nodeContent.Add(node.ToString());
            }

            resource1.Save("C:\\FinalResources.resx");

在删除语句中,我得到一个 InvalidOperationException - “父级丢失。”:

我做错了吗?

【问题讨论】:

  • 您应该定义在您的上下文中什么是“重复”。你如何比较两个 xml 条目?通过name 属性?按标签名称?按内容?
  • 如果还有人在寻找这个问题的答案,那么请看这里。 stackoverflow.com/questions/982597/…

标签: c# xml linq-to-xml


【解决方案1】:

您需要定义一个EqualityComparer&lt;XElement&gt;,这将使您能够使用标准的 LINQ 运算符。

所以,作为一个简单的例子,我创建了这个:

public class ElementComparer : EqualityComparer<XElement>
{
    public override int GetHashCode(XElement xe)
    {
        return xe.Name.GetHashCode() ^ xe.Value.GetHashCode();
    }

    public override bool Equals(XElement xe1, XElement xe2)
    {
        var @return = xe1.Name.Equals(xe2.Name);
        if (@return)
        {
            @return = xe1.Value.Equals(xe2.Value); 
        }
        return @return;
    }
}

所以我可以从这两个 XML 文档开始:

<xs>
    <x>D</x>
    <x>A</x>
    <x>B</x>
</xs>

<xs>
    <x>E</x>
    <x>B</x>
    <x>C</x>
</xs>

然后这样做:

var xml1 = XDocument.Parse(@"<xs><x>D</x><x>A</x><x>B</x></xs>");
var xml2 = XDocument.Parse(@"<xs><x>E</x><x>B</x><x>C</x></xs>");

xml1.Root.Add(
    xml2.Root.Elements("x")
        .Except(xml1.Root.Elements("x"), new ElementComparer()));

然后xml1 将如下所示:

<xs>
    <x>D</x>
    <x>A</x>
    <x>B</x>
    <x>E</x>
    <x>C</x>
</xs>

【讨论】:

  • @Si8 - 究竟是什么不起作用?所有代码都应该使用简单的复制粘贴。
  • metge 没有将第二个 XDocument 添加到第一个,但我走了不同的路线。谢谢,
  • @Si8 - 我认为您可能需要检查您的代码。我刚刚将代码复制/粘贴到一个新项目中,它对我来说效果很好。如果可以的话,我想追查这个问题。
  • 谢谢。我的 XDocument 是一个 Sharepoint ATOM 提要,所以也许这就是原因。
  • @Si8 - ATOM 提要 XML 是否包含除默认命名空间之外的命名空间?
【解决方案2】:

嗯,最直接的方法是:

var resource1 = XDocument.Load("C:\\Resources.resx");
var resource2 = XDocument.Load("C:\\Resources2.resx");

foreach (XElement node in resource2.Root.Elements())
{                    
    if (resource1.Root.Contains(node)) continue;
    resource1.Add(node);
}

resource1.Save("C:\\FinalResources.resx");


public static class XElementExtensions
{
    public static bool Contains(this XElement root, XElement e)
    {
        //or w/e equality logic you need
        return root.Elements().Any(x => x.ToString().Equals(e.ToString()));
    }
}

这只会合并第一级条目。如果您需要深度合并,则必须设置一个简单的递归(对子元素使用相同的循环)。

【讨论】:

    【解决方案3】:

    resource1.Remove();被调用两次,它的作用是删除根元素。所以第二次不再有要删除的根元素,从而引发异常。

    【讨论】:

      猜你喜欢
      • 2017-06-05
      • 1970-01-01
      • 1970-01-01
      • 2010-12-07
      • 1970-01-01
      • 2015-06-09
      • 2011-05-25
      相关资源
      最近更新 更多