将两个XML文件合并成一个的最快方法是什么?

37
如果我有两个字符串 xml1 和 xml2,它们都表示相同格式的 xml。那么,最快的将它们合并在一起的方法是什么?格式不重要,但我想知道如何消除 <和> 。

xml1:

<?xml version="1.0" encoding="utf-8"?>
<AllNodes>
   <NodeA>
      <NodeB>test1</NodeB>
      <NodeB>test2</NodeB>
   </NodeA>
</AllNodes>

xm2 :

->

xm2:

<?xml version="1.0" encoding="utf-8"?>
<AllNodes>
   <NodeA>
      <NodeB>test6</NodeB>
      <NodeB>test7</NodeB>
   </NodeA>
   <NodeA>
      <NodeB>test99</NodeB>
      <NodeB>test23</NodeB>
   </NodeA>
</AllNodes>

并且需要类似于这样:

<?xml version="1.0" encoding="utf-8"?>
    <AllNodes>
          <NodeA>
              <NodeB>test1</NodeB>
              <NodeB>test2</NodeB>
          </NodeA>
         <NodeA>
              <NodeB>test6</NodeB>
              <NodeB>test7</NodeB>
           </NodeA>
           <NodeA>
              <NodeB>test99</NodeB>
              <NodeB>test23</NodeB>
           </NodeA>
    </AllNodes>

为了满足自己的需求,我快速编写了一个合并(web.config)XML文件的工具(http://pastebin.com/FzkRczZ6)。此外,还有一个配置文件合并工具(https://configmerger.codeplex.com/)。 - Uwe Keim
11个回答

55

最简单的方法是使用LINQ to XML。你可以根据需要使用UnionConcat

var xml1 = XDocument.Load("file1.xml");
var xml2 = XDocument.Load("file2.xml");

//Combine and remove duplicates
var combinedUnique = xml1.Descendants("AllNodes")
                          .Union(xml2.Descendants("AllNodes"));

//Combine and keep duplicates
var combinedWithDups = xml1.Descendants("AllNodes")
                           .Concat(xml2.Descendants("AllNodes"));

1
问题是我有两个字符串而不是两个 XML 文件。这两个字符串表示从另一个站点发送的 XML 元素。有没有办法将我的字符串转换为 XElement 或类似的东西?这样我就可以遍历它的元素了? - paradisonoir
2
要将您的字符串转换为XElement,您可以使用XElement.Parse(yourstring)。 - Jose Basilio
2
您也可以使用XDocument.Parse(yourstring)将字符串转换为XDocument。 - Blegger
11
Jose Basilio的回答很好,但不够完整,它将创建一个包含两个"AllNodes"元素的XElement IEnumerator。 一个更准确的答案是(如果重复不是问题):xml1.Descendants("NodeA").LastOrDefault().AddAfterSelf(xml2.Descendants("NodeA")); xml1.Save(); - Vlax
@Vlax,这是一个很棒的完成,你能告诉我如何将此文件保存为新文件吗?比如说如果用xml1替换了xml2,我需要将结果保存为xml3,这可行吗? - Nachiappan R
1
如何从合并后的内容中获取XDocument? - jjxtra

9
一种XSLT转换可以实现它:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="pXml1" select="''" />
  <xsl:param name="pXml2" select="''" />
  <xsl:param name="pRoot" select="'root'" />

  <xsl:template match="/">
    <xsl:variable name="vXml1" select="document($pXml1)" />
    <xsl:variable name="vXml2" select="document($pXml2)" />

    <xsl:element name="{$pRoot}">
      <xsl:copy-of select="$vXml1/*/*" />
      <xsl:copy-of select="$vXml2/*/*" />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

将文件名和新根元素的名称作为参数传递。

适用于任何XML文档,例如空文档。


非常感谢。您的解决方案看起来很棒,但您知道如何将该模式应用于XML文档吗? - paradisonoir
例如,https://dev59.com/d0bRa4cB1Zd3GeqP04CR 的被接受答案展示了如何在.NET中实现它。 - Tomalak
另一方面,如果您完全不了解XSLT,它可能不是您的理想解决方案。我也不知道它在与本主题中建议的其他方法相比如何表现。 - Tomalak

4
这是合并xml文件最快、最干净的方法。
XElement xFileRoot = XElement.Load(file1.xml);
XElement xFileChild = XElement.Load(file2.xml);
xFileRoot.Add(xFileChild);
xFileRoot.Save(file1.xml);

1
这会保留还是删除重复项? - jjxtra

3
如果您想使用XmlDocument,请尝试以下方法:
 var lNode = lDoc1.ImportNode(lDoc2.DocumentElement.FirstChild, true);
 lDoc1.DocumentElement.AppendChild(lNode);

3
var doc= XDocument.Load("file1.xml");
var doc1= XDocument.Load("file2.xml");
doc.Root.Add(doc2.Root.Elements());

最简单、最好的方法就是这样做。 - Kishan Vaishnav

2
如果您可以保证这种格式,那么可以通过字符串操作将它们组合起来:
  • 读取第一个文件,在"</AllNodes>"之前保留所有内容
  • 读取第二个文件,删除"<AllNodes>"之前的部分
  • 将这些字符串组合在一起。
由于不需要解析,因此这应该是最快的方法。
const string RelevantTag = "AllNodes";

string xml1 = File.ReadAllText(xmlFile1);
xml1 = xml1.Substring(0, xml.LastIndexOf("</" + RelevantTag + ">"));

string xml2 = File.ReadAllText(xmlFile2);
xml2 = xml2.Substring(xml.IndexOf("<" + RelevantTag + ">") + "<" + RelevantTag + ">".Length, xml1.Length);

File.WriteAllText(xmlFileCombined, xm1 + xml2);

话虽如此,但我总是更喜欢选择安全的方式而不是快速的方式。


1

对我来说最好的解决方案是,基于Jose Basilio的答案,稍作修改,

var combinedUnique = xml1.Descendants()
    .Union(xml2.Descendants());
combinedUnique.First().Save(#fullName)

1
感谢@user3510339提供的代码,但是它只保存了第一个XML文件的内容。TIA - saadasharif

1

你有两个基本选项:

  1. 解析xml,组合数据结构,再序列化回xml。

  2. 如果你知道结构,可以使用一些基本的字符串操作来处理它。例如,在上面的示例中,您可以将两个xml块中的所有节点内部放入单个allnodes块中并完成。


0
在我的情况下,主要解决方案效果不佳,区别在于我有一个包含数千个文件的列表,当我尝试将其中一个元素与第一个元素合并时,会出现内存溢出异常。为了解决这个奇怪的内存问题并使其顺利运行,我添加了一个空模板和一个空行(在此示例中为NodeA)
我在另一个进程中保存文档。
XDocument xmlDocTemplate = GetXMLTemplate(); -- create an empty document with the same root and empty row element (NodeA), everything will be merge here.
List<XElement> lstxElements = GetMyBunchOfXML();

foreach (var xmlElement lstxElements)
{
    xmlDocTemplate
        .Root
        .Descendants("NodeA")
        .LastOrDefault()
        .AddAfterSelf(xmlElement.Descendants("NodeA"));
}

0

既然你要求“最快”的方法:

如果(且仅当)XML结构始终保持一致:(这是伪代码)

string xml1 = //get xml1 somehow
string xml2 = //get xml2 somehow
xml1 = replace(xml1, "<?xml version=\"1.0\" encoding=\"utf-8\"?>", "");
xml1 = replace(xml1, "<allnodes>", "");
xml1 = replace(xml1, "</allnodes>", "");
xml2 = replace(xml2, "<allnodes>", "<allnodes>\n" + xml1);

这是一个巨大的黑科技,但速度很快。当你的同事发现它时,期待在TheDailyWTF上看到它。


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