如何合并XML文件?

我有两个xml文件,它们都具有相同的模式,我想合并到一个xml文件中。 是否有捷径可寻?

例如,

          

+

           

=包含的新文件

               

“自动XML合并”听起来像是一个相对简单的要求,但是当你深入研究所有细节时,它会变得非常复杂。 与c#或XSLT合并对于更具体的任务将更容易,例如在EF模型的答案中。 使用工具来协助手动合并也是一种选择(参见这个问题 )。

作为参考(并提供关于复杂性的概念),这是来自Java世界的开源示例: XML合并变得简单

回到原来的问题。 任务规范中几乎没有大的灰色区域:当2个元素应该被认为是等价的 (具有相同的名称,匹配选定的或所有的属性,或者在父元素中也具有相同的位置); 当原始或合并的XML具有多个等价元素等时如何处理情况

下面的代码是假设的

  • 我们现在只关心元素
  • 如果元素名称,属性名称和属性值匹配,则元素是等效的
  • 元素没有多个具有相同名称的属性
  • 合并文档中的所有等效元素将与源XML文档中的第一个等效元素组合。

 // determine which elements we consider the same // private static bool AreEquivalent(XElement a, XElement b) { if(a.Name != b.Name) return false; if(!a.HasAttributes && !b.HasAttributes) return true; if(!a.HasAttributes || !b.HasAttributes) return false; if(a.Attributes().Count() != b.Attributes().Count()) return false; return a.Attributes().All(attA => b.Attributes(attA.Name) .Count(attB => attB.Value == attA.Value) != 0); } // Merge "merged" document B into "source" A // private static void MergeElements(XElement parentA, XElement parentB) { // merge per-element content from parentB into parentA // foreach (XElement childB in parentB.DescendantNodes()) { // merge childB with first equivalent childA // equivalent childB1, childB2,.. will be combined // bool isMatchFound = false; foreach (XElement childA in parentA.Descendants()) { if (AreEquivalent(childA, childB)) { MergeElements(childA, childB); isMatchFound = true; break; } } // if there is no equivalent childA, add childB into parentA // if (!isMatchFound) parentA.Add(childB); } } 

它将使用原始XML片段产生所需的结果,但如果输入XML更复杂并且具有重复元素,则结果将更加有趣:

 public static void Test() { var a = XDocument.Parse(@"           "); var b = XDocument.Parse(@"                "); MergeElements(a.Root, b.Root); Console.WriteLine("Merged document:\n{0}", a.Root); } 

这是合并后的文档,显示了文档B中的等效元素如何组合在一起:

                     

如果格式总是这样,则此方法没有任何问题:

从第一个文件中删除最后两行,并在删除前两行时附加第二个文件。

看一下可以删除前两行的Linux命令headtail

这是一个简单的XSLT转换(你应用于文档a.xml):

             

vimdiff file_a file_b只是一个例子

当我在Windows上时,BeyondCompare是最受欢迎的http://www.scootersoftware.com/

我最终使用C#并创建了一个脚本。 当我提出问题时,我知道我可以做到这一点,但我想知道是否有更快的方法来实现这一点,因为我从未真正使用过XML。

该脚本遵循以下方针:

 var a = new XmlDocument(); a.Load(PathToFile1); var b = new XmlDocument(); b.Load(PathToFile2); MergeNodes( a.SelectSingleNode(nodePath), b.SelectSingleNode(nodePath).ChildNodes, a); a.Save(PathToFile1); 

MergeNodes()看起来像这样:

 private void MergeNodes(XmlNode parentNodeA, XmlNodeList childNodesB, XmlDocument parentA) { foreach (XmlNode oNode in childNodesB) { // Exclude container node if (oNode.Name == "#comment") continue; bool isFound = false; string name = oNode.Attributes["Name"].Value; foreach (XmlNode child in parentNodeA.ChildNodes) { if (child.Name == "#comment") continue; // If node already exists and is unchanged, exit loop if (child.OuterXml== oNode.OuterXml&& child.InnerXml == oNode.InnerXml) { isFound = true; Console.WriteLine("Found::NoChanges::" + oNode.Name + "::" + name); break; } // If node already exists but has been changed, replace it if (child.Attributes["Name"].Value == name) { isFound = true; Console.WriteLine("Found::Replaced::" + oNode.Name + "::" + name); parentNodeA.ReplaceChild(parentA.ImportNode(oNode, true), child); } } // If node does not exist, add it if (!isFound) { Console.WriteLine("NotFound::Adding::" + oNode.Name + "::" + name); parentNodeA.AppendChild(parentA.ImportNode(oNode, true)); } } } 

它不完美 – 我必须手动指定我想要合并的节点,但是我可以快速轻松地组合在一起,因为我几乎不了解XML,我很高兴:)

它实际上更好,它只合并指定的节点,因为我用它来合并Entity Framework的edmx文件,我真的只想合并SSDL,CDSL和MSL节点。

您可以这样做的方法是使用xml加载数据集并合并数据集。

  Dim dsFirst As New DataSet() Dim dsMerge As New DataSet() ' Create new FileStream with which to read the schema. Dim fsReadXmlFirst As New System.IO.FileStream(myXMLfileFirst, System.IO.FileMode.Open) Dim fsReadXmlMerge As New System.IO.FileStream(myXMLfileMerge, System.IO.FileMode.Open) Try dsFirst.ReadXml(fsReadXmlFirst) dsMerge.ReadXml(fsReadXmlMerge) Dim str As String = "Merge Table(0) Row Count = " & dsMerge.Tables(0).Rows.Count str = str & Chr(13) & "Merge Table(1) Row Count = " & dsMerge.Tables(1).Rows.Count str = str & Chr(13) & "Merge Table(2) Row Count = " & dsMerge.Tables(2).Rows.Count MsgBox(str) dsMerge.Merge(dsFirst, True) DataGridParent.DataSource = dsMerge DataGridParent.DataMember = "rulefile" DataGridChild.DataSource = dsMerge DataGridChild.DataMember = "rule" str = "" str = "Merge Table(0) Row Count = " & dsMerge.Tables(0).Rows.Count str = str & Chr(13) & "Merge Table(1) Row Count = " & dsMerge.Tables(1).Rows.Count str = str & Chr(13) & "Merge Table(2) Row Count = " & dsMerge.Tables(2).Rows.Count MsgBox(str)