两个XML文件的智能合并

本文关键字:智能 合并 文件 XML 两个 | 更新日期: 2023-09-27 17:58:15

我正在尝试合并XML,但我有非常具体的要求。我有以下两个XML文件:

<msg action="getcustomlists" class="lookup" ul="1">
  <host name="hp"/>
</msg>

<msg action="getcustomlists" class="newLookup" lac="statements">
  <environment type="lab">
    <login id="manual" />
  </environment>
</msg>

我想把这两个文件合并成这样:

<msg action="getcustomlists" class="newLookup" lac="statements" ul="1">
  <host name="hp"/>
  <environment type="lab">
    <login id="manual" />
  </environment>
</msg>

换句话说,我需要合并属性和元素。如果因为属性重复而发生冲突,我们只需要用第二个文件覆盖结果。

我尝试过DataSet.Merge(DataSet),但没有得到所需的结果。请帮忙。

谢谢,哈里特

两个XML文件的智能合并

您可以使用XmlDocument,打开两个源,遍历节点并将其合并到新的XmlDocument中。

此外,使用XmlDocument,您可以使用LINQ测试冲突,这简化了此任务。

    XmlDocument MergeDocs(string SourceA, string SourceB)
    {
        XmlDocument docA = new XmlDocument();
        XmlDocument docB = new XmlDocument();
        XmlDocument merged = new XmlDocument();
        docA.LoadXml(SourceA);
        docB.LoadXml(SourceB);
        var childsFromA = docA.ChildNodes.Cast<XmlNode>();
        var childsFromB = docB.ChildNodes.Cast<XmlNode>();
        var uniquesFromA = childsFromA.Where(ch => childsFromB.Where(chb => chb.Name == ch.Name).Count() == 0);
        var uniquesFromB = childsFromB.Where(ch => childsFromA.Where(chb => chb.Name == ch.Name).Count() == 0);
        foreach (var unique in uniquesFromA)
            merged.AppendChild(DeepCloneToDoc(unique, merged));
        foreach (var unique in uniquesFromA)
            merged.AppendChild(DeepCloneToDoc(unique, merged));
        var Duplicates = from chA in childsFromA
                         from chB in childsFromB
                         where chA.Name == chB.Name
                         select new { A = chA, B = chB };
        foreach (var grp in Duplicates)
            merged.AppendChild(MergeNodes(grp.A, grp.B, merged));
        return merged;
    }
    XmlNode MergeNodes(XmlNode A, XmlNode B, XmlDocument TargetDoc)
    {
        var merged = TargetDoc.CreateNode(A.NodeType, A.Name, A.NamespaceURI);
        foreach (XmlAttribute attrib in A.Attributes)
            merged.Attributes.Append(TargetDoc.CreateAttribute(attrib.Prefix, attrib.LocalName, attrib.NamespaceURI));
        var fromA = A.Attributes.Cast<XmlAttribute>();
        var fromB = B.Attributes.Cast<XmlAttribute>();
        var toAdd = fromB.Where(attr => fromA.Where(ata => ata.Name == attr.Name).Count() == 0);
        foreach (var attrib in toAdd)
            merged.Attributes.Append(TargetDoc.CreateAttribute(attrib.Prefix, attrib.LocalName, attrib.NamespaceURI));
        var childsFromA = A.ChildNodes.Cast<XmlNode>();
        var childsFromB = B.ChildNodes.Cast<XmlNode>();
        var uniquesFromA = childsFromA.Where(ch => childsFromB.Where(chb => chb.Name == ch.Name).Count() == 0);
        var uniquesFromB = childsFromB.Where(ch => childsFromA.Where(chb => chb.Name == ch.Name).Count() == 0);
        foreach (var unique in uniquesFromA)
            merged.AppendChild(DeepCloneToDoc(unique, TargetDoc));
        foreach (var unique in uniquesFromA)
            merged.AppendChild(DeepCloneToDoc(unique, TargetDoc));
        var Duplicates = from chA in childsFromA
                         from chB in childsFromB
                         where chA.Name == chB.Name
                         select new { A = chA, B = chB };
        foreach(var grp in Duplicates)
            merged.AppendChild(MergeNodes(grp.A, grp.B, TargetDoc));
        return merged;
    }
    XmlNode DeepCloneToDoc(XmlNode NodeToClone, XmlDocument TargetDoc)
    {
        var newNode = TargetDoc.CreateNode(NodeToClone.NodeType, NodeToClone.Name, NodeToClone.NamespaceURI);
        foreach (XmlAttribute attrib in NodeToClone.Attributes)
            newNode.Attributes.Append(TargetDoc.CreateAttribute(attrib.Prefix, attrib.LocalName, attrib.NamespaceURI));
        foreach (XmlNode child in NodeToClone.ChildNodes)
            newNode.AppendChild(DeepCloneToDoc(NodeToClone, TargetDoc));
        return newNode;
    }

注意,我还没有测试过,只是根据记忆进行的,但你已经知道了如何进行。

使用LINQ到XML+XDocument

创建了一个扩展方法:

public static XDocument MergeXml(this XDocument xd1, XDocument xd2)
{
    return new XDocument(
        new XElement(xd2.Root.Name,
            xd2.Root.Attributes()
                .Concat(xd1.Root.Attributes())
                .GroupBy (g => g.Name)
                .Select (s => s.First()),
            xd2.Root.Elements()
                .Concat(xd1.Root.Elements())
                .GroupBy (g => g.Name)
                .Select (s => s.First())));
}

使用它:

var xd1 = XDocument.Load("test1.xml").MergeXml(XDocument.Load("test2.xml"));

这将产生:

<msg action="getcustomlists" class="newLookup" lac="statements" ul="1">
  <environment type="lab">
    <login id="manual" />
  </environment>
  <host name="hp" />
</msg>