Smart merging of two XML files

时光毁灭记忆、已成空白 提交于 2021-02-20 02:59:05

问题


I am trying to merge XML but I have very specific requirements. I have following two XML files:

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

and

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

I want to merge these two files to make something like this:

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

In other words, I need to merge attributes and the elements. In case, there is a conflict because of a duplicate attribute, we just need to override the results with the second file.

I have tried DataSet.Merge(DataSet). but that is not giving me desired results. Please help.

Thanks, Harit


回答1:


You can use XmlDocument, open both sources, iterate through nodes and merge it in a new XmlDocument.

Also, with XmlDocument you can use LINQ to test for collisions, what simplifies this task.

    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;

    }

Note I haven't tested it, done just from memory but you get the idea about how to go.




回答2:


Using LINQ-to-XML + XDocument

Created an extension method that does it:

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

To use it:

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

This will produce:

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


来源:https://stackoverflow.com/questions/23071986/smart-merging-of-two-xml-files

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!