1

I need to 'merge' 2 xml files together but take into account elements that appear in both and merge attributes that are different in each. Here's an example of what I mean:

Input: Xml1

<?xml version="1.0"?>
<Style Width="1024" Height="768">
    <BaseStyle Width="1024" Height="768" BackgroundPath="./Images/BackgroundAAA.png"/>
    <Styles>
        <LabelStyle ID="Label1" Font="Tahoma" Bold="false" /> <!-- exists in both -->
        <LabelStyle ID="Label2" Font="Tahoma" Bold="false" /> <!-- exists in both -->
        <LabelStyle ID="Label3" Font="Tahoma" Bold="false" /> <!-- unique -->
  </Styles>
</Style>

Input: Xml2

<?xml version="1.0"?>
<Style Width="1024" Height="768">
    <BaseStyle Width="1024" Height="768" BackgroundPath="./Images/BackgroundBBB.png"/>
    <Styles>
        <LabelStyle ID="Label1" Font="Arial" Bold="true" /> <!-- exists in both -->
        <LabelStyle ID="Label2" Font="Arial" /> <!-- exists in both -->
        <LabelStyle ID="Label4" Font="Arial" Bold="false" /> <!-- unique -->
    </Styles>
</Style>

and I need to get the following as output: -

Result: Xml3

<?xml version="1.0"?>
<Style Width="1024" Height="768">
    <BaseStyle Width="1024" Height="768" BackgroundPath="./Images/BackgroundBBB.png"/> <!-- has overwritten Xml1 -->
    <Styles>
        <LabelStyle ID="Label1" Font="Arial" Bold="true" /> <!-- has merged Xml1 & Xml2 with Xml2 taking precedence -->
        <LabelStyle ID="Label2" Font="Arial" Bold="false" /> <!-- has merged Xml1 & Xml2 with Xml2 taking precedence (note Bold attribute is present) -->
        <LabelStyle ID="Label3" Font="Tahoma" Bold="false" /> <!-- unique -->
        <LabelStyle ID="Label4" Font="Arial" Bold="false" /> <!-- unique -->
    </Styles>
</Style>

You'll notice from above that anything unique gets merged and anything common (using ID as the key) is updated with Xml2 values.

I've been reading some of the many great questions up here on how to merge 2 xml files using LINQ but none of them really satify my requirement. They all seems to talk about taking A and B and ending up with A+B.

Is there an easy way to do this using LINQ?

Thanks in advance!

Slippy
  • 165
  • 1
  • 10

1 Answers1

2

for example:

class Program {
    static void Main(string[] args) {
        StringReader sr = new StringReader("<?xml version=\"1.0\"?><Style Width=\"1024\" Height=\"768\"><BaseStyle Width=\"1024\" Height=\"768\" BackgroundPath=\"./Images/BackgroundBBB.png\"/><Styles><LabelStyle ID=\"Label1\" Font=\"Arial\" Bold=\"true\" /> <!-- exists in both --><LabelStyle ID=\"Label2\" Font=\"Arial\" /> <!-- exists in both --><LabelStyle ID=\"Label3\" Font=\"Arial\" Bold=\"false\" /> <!-- unique --></Styles></Style>"); 
        XDocument xdoc1 = XDocument.Load(sr);
        sr.Close();
        sr = new StringReader("<?xml version=\"1.0\"?><Style Width=\"1024\" Height=\"768\"><BaseStyle Width=\"1024\" Height=\"768\" BackgroundPath=\"./Images/BackgroundBBB.png\"/><Styles><LabelStyle ID=\"Label1\" Font=\"Arial\" Bold=\"true\" /> <!-- exists in both --><LabelStyle ID=\"Label2\" Font=\"Arial\" /> <!-- exists in both --><LabelStyle ID=\"Label4\" Font=\"Arial\" Bold=\"false\" /> <!-- unique --></Styles></Style>");
        XDocument xdoc2 = XDocument.Load(sr);
        sr.Close();

        sr = new StringReader("<?xml version=\"1.0\"?><Style Width=\"1024\" Height=\"768\"><BaseStyle Width=\"1024\" Height=\"768\" BackgroundPath=\"./Images/BackgroundBBB.png\"/><Styles></Styles></Style>");
        XDocument xDocDest = XDocument.Load(sr);
        sr.Close();
        XElement xeDest = xDocDest.Root.Descendants("Styles").Single();

        XElement[] x2s = (from x in xdoc2.Root.Descendants("LabelStyle") orderby x.Attribute("ID").Value select x).ToArray() ;

        Int32 iCurrentX2 = 0;
         XElement xeCurrentX2 = (x2s.Count() > 1) ? x2s[0] : null;
        foreach (XElement x1 in from x in xdoc1.Root.Descendants("LabelStyle") orderby x.Attribute("ID").Value select x) {
            if (xeCurrentX2 == null || String.Compare(x1.Attribute("ID").Value, xeCurrentX2.Attribute("ID").Value) < 0) {
                xeDest.Add(x1);
            } else {
                while (iCurrentX2 < x2s.Count() && String.Compare(x1.Attribute("ID").Value, xeCurrentX2.Attribute("ID").Value) >= 0) {
                    xeDest.Add(xeCurrentX2);
                    xeCurrentX2 = (++iCurrentX2 < x2s.Count()) ? x2s[iCurrentX2] : null;
                }
            }
        }
        while (iCurrentX2 < x2s.Count()) {
            xeDest.Add(x2s[iCurrentX2++]);
        }

        Console.WriteLine(xDocDest.ToString());
    }
}

this is not perfectly finalized, but I think the structure is here

tschmit007
  • 7,559
  • 2
  • 35
  • 43
  • Thanks @tschmit007 - really really close :)!! Just a couple of questions: - 1. the output does the merge of the elements but isn't picking up the fact that Xml1 and Xml2 have Label2 and the font should be updated to Arial (from Tahoma). I think the condition you have here is 'it's it's not there, add it.' which almost works for me! 2. On Xml1:BaseStyle the attribute BackgroundPath is "BackgroundAAA.png" while on Xml2 it's BackgroundBBB.png. Is it possible to 'merge these so the end result is as per Xml3? Thank you very much for replying - really appreciate it! – Slippy Sep 10 '12 at 17:03