36

If I have two string of xml1 and xml2 which both represent xml in the same format. What is the fastest way to combine these together? The format is not important, but I just want to know how can I get rid off or ?

xml1 :

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

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>

and have something like this :

<?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>
paradisonoir
  • 2,892
  • 9
  • 30
  • 41
  • For my own needs, [I've quickly written something to merge (web.config) XML files](http://pastebin.com/FzkRczZ6). There is also a [Configuration File Merger tool](https://configmerger.codeplex.com/). – Uwe Keim Jun 30 '15 at 04:41

11 Answers11

54

The easiest way to do this is using LINQ to XML. You can use either Union or Concat depending on your needs.

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"));
Jose Basilio
  • 50,714
  • 13
  • 121
  • 117
  • 1
    well, the problem is I have two strings rather than two xml file. These two string represent xml elements which are sent from another station. Is there any way to cast my string to XElement or something like that?So I can walkthrough its elements? – paradisonoir Jun 11 '09 at 19:16
  • 2
    To convert your string to XElement, you can use XElement.Parse(yourstring) – Jose Basilio Jun 11 '09 at 20:10
  • 2
    You can convert your string to a XDocument as well using XDocument.Parse(yourstring) – Blegger Aug 27 '09 at 17:09
  • 11
    The answer from Jose Basilio is very good but incomplete, it will create a XElement IEnumerator with 2 "AllNodes" elements. A more accurate answer would be (if duplicates are not an issue): `xml1.Descendants("NodeA").LastOrDefault().AddAfterSelf(xml2.Descendants("NodeA")); xml1.Save();` – Vlax Oct 29 '12 at 09:59
  • @Vlax it was a wounderful completion, can you tell me how to save this file as a new file? like if xml2 is replaced with xml1 and I need to save as the result as xml3, is it possible? – Nachiappan R Oct 27 '15 at 18:32
  • 1
    How to get an XDocument back from combined? – jjxtra Dec 12 '20 at 03:16
9

An XSLT transformation could do it:

<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>

Pass in the names of the files as parameters, as well as the name of the new root element.

Apply to any XML document, e.g. an empty one.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thank you so much. Your solution looks great, but do you know how I can apply that schema to an xml document? – paradisonoir Jun 11 '09 at 18:07
  • For example, the accepted answer to http://stackoverflow.com/questions/529374/whats-the-property-way-to-transform-with-xsl-without-html-encoding-my-final-outp shows how to do it in .NET – Tomalak Jun 11 '09 at 18:19
  • On the other hand - if you have no knowledge of XSLT whatsoever, it might not be the ideal solution for you. And I don't know how it will perform in comparison to the other ways suggested in this thread. – Tomalak Jun 11 '09 at 18:21
4

This is the fastest and cleanest way to merge xml files.

XElement xFileRoot = XElement.Load(file1.xml);
XElement xFileChild = XElement.Load(file2.xml);
xFileRoot.Add(xFileChild);
xFileRoot.Save(file1.xml);
Rudy Hinojosa
  • 1,448
  • 14
  • 15
3
var doc= XDocument.Load("file1.xml");
var doc1= XDocument.Load("file2.xml");
doc.Root.Add(doc2.Root.Elements());
bugmagnet
  • 7,631
  • 8
  • 69
  • 131
Heba El-Fadly
  • 281
  • 1
  • 5
  • 17
3

If you want to use the XmlDocument, try this

 var lNode = lDoc1.ImportNode(lDoc2.DocumentElement.FirstChild, true);
 lDoc1.DocumentElement.AppendChild(lNode);
Vasu Balakrishnan
  • 1,751
  • 10
  • 15
2

If you can guarantee this format you can combine them by doing string manipulation:

  • Read the first file, keep everything before "</AllNodes>"
  • Read the second file, remove the part up to "<AllNodes>"
  • Combine those strings.

This should be the fastest way since no parsing is needed.

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

That said I would always prefer the safe way to the fast way.

VVS
  • 19,405
  • 5
  • 46
  • 65
1

Best solution to me, based on Jose Basilio answer, slightly modified,

var combinedUnique = xml1.Descendants()
    .Union(xml2.Descendants());
combinedUnique.First().Save(#fullName)
Andrei Herford
  • 17,570
  • 19
  • 91
  • 225
elhumidio
  • 11
  • 2
1

You have two basic options:

  1. Parse the xml, combine the data structures, serialize back to xml.

  2. If you know the structure, use some basic string manipulation to hack it. For example, in the example above you could take the inside of allnodes in the two xml blocks and put them in a single allnodes block and be done.

Alan Jackson
  • 6,361
  • 2
  • 31
  • 32
0

In my case the main solution did not work well, the difference was that I had a List for a thousands of files when I take one element and try to merge with the first element I get OutOfMemory exception, I added an empty template with and empty row (NodeA in this case) to solve the weird problem of the memory and run smoothly.

I save the document in other process

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

If I were doing this (using C#), I would create a class that I can deserialize this XML to (you can use xsd.exe to do this), and then loop through all the nodes in the object representing the first piece of XML and "Add" them to the AllNodes property of the object representing the second XML.

Then serialize the second class back out the XML, and it should look like your 3rd example.

Sam Schutte
  • 6,666
  • 6
  • 44
  • 54
0

Since you asked for the fastest:

If (and only if) the xml structure is always consistent: (this is pseudo code)

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

It's a giant hack but it's fast. Expect to see it on TheDailyWTF when your colleagues find it.

rein
  • 32,967
  • 23
  • 82
  • 106