如何合并两个XDocuments并删除重复项

3

我有两个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");

在 remove 语句中,我收到了一个 InvalidOperationException 异常 - "The parent is missing.":
我做错了什么吗?

你需要定义在你的上下文中,“重复”是什么意思。你如何比较两个xml条目?通过“名称”属性?通过标签名称?通过内容? - Nikita B
如果仍有人在寻找这个问题的答案,请看这里。https://dev59.com/w3NA5IYBdhLWcg3wYMx- - Khurram Hassan
3个回答

2

您需要定义一个EqualityComparer<XElement>,以便您可以使用标准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>

这位程序员没有将第二个XDocument添加到第一个中,但我选择了不同的方法。谢谢。 - Si8
@Si8 - 我认为你可能需要检查一下你的代码。我刚刚将代码复制/粘贴到一个新项目中,它对我来说运行得很好。如果可以的话,我想追踪一下这个问题。 - Enigmativity
谢谢。我的XDocument是一个Sharepoint ATOM feed,可能是这个原因。 - Si8
@Si8 - ATOM feed XML中是否包含除默认命名空间以外的其他命名空间? - Enigmativity
是的...有三个不同的。 - Si8
显示剩余2条评论

1

好的,最直接的方法是:

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

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

0

resource1.Remove(); 被调用了两次,它的作用是移除根元素。因此第二次没有根元素可以移除,从而抛出异常。


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