Avi
Avi

Reputation: 1183

Merge XML based node value

I wish to merge two XML files, similar to What is the fastest way to combine two xml files into one

but I cannot wrap my head around how to group and merge them based on of the node values (Configuration node's Domain node value) I am trying it with Linq, but it doesn't make it easier, even the group by and where clause is there.

Basically I wish all Component nodes (duplicates are allowed) to be listed under the same Configuration node, which Domain name node values are equal.

In other words with the below example: the result XML has two Configuration nodes, one with Domain: MyDom01 the other is Domain: MyDom02 and under each configuration I have one Components node with all the Component listed.

Is that even possible?

One.XML

<System>
    <Configurations>
    
        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>16</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>8</Size>
                </Component>
            </Components>
        </Configuration>

        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>HDD</Name>
                    <Size>1</Size>
                </Component>
            </Components>
        </Configuration>
                    
        <Configuration>
            <Domain>MyDom02</Domain>
            <Components>
                <Component>
                    <Name>CPU</Name>
                    <Size>12</Size>
                </Component>
            </Components>
        </Configuration>

    </Configurations>
</System>

Another.XML

<System>
    <Configurations>
    
        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>128</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>
            </Components>
        </Configuration>
                    
        <Configuration>
            <Domain>MyDom02</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>32</Size>
                </Component>
            </Components>
        </Configuration>

    </Configurations>
</System>

Merged.XML:

<System>
    <Configurations>
    
        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>16</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>8</Size>
                </Component>

                <Component>
                    <Name>HDD</Name>
                    <Size>1</Size>
                </Component>

                <Component>
                    <Name>Memory</Name>
                    <Size>128</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>                
            </Components>
        </Configuration>
                    
        <Configuration>
            <Domain>MyDom02</Domain>
            <Components>
                <Component>
                    <Name>CPU</Name>
                    <Size>12</Size>
                </Component>
                <Component>
                    <Name>Memory</Name>
                    <Size>32</Size>
                </Component>
            </Components>
        </Configuration>

    </Configurations>
</System>

Upvotes: 1

Views: 135

Answers (2)

jdweng
jdweng

Reputation: 34419

Using Xml Linq :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        const string FILENAME1 = @"c:\temp\test1.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            XElement configurations = doc.Descendants("Configuration").FirstOrDefault();
            Dictionary<string, XElement> dict = doc.Descendants("Configuration")
                .GroupBy(x => (string)x.Element("Domain"), y => y)
                .ToDictionary(x => x.Key, y => y.FirstOrDefault());
       

            XDocument doc1 = XDocument.Load(FILENAME1);

            XElement configurations1 = doc1.Descendants("Configurations").FirstOrDefault();
            foreach (XElement configuration1 in configurations1.Elements("Configuration"))
            {
                string domain = (string)configuration1.Element("Domain");
                if (dict.ContainsKey(domain))
                {
                    XElement config = dict[domain];
                    config.Element("Components").Add(configuration1.Descendants("Component"));
                }
                else
                {
                    configurations.Add(configuration1);
                    dict.Add(domain, configuration1);
                }
            }
        }
    }
}

Upvotes: 1

har07
har07

Reputation: 89295

You can try to find <Components> element with matching <Domain> in XML1 then add <Component> elements from XML2 to it, and add <Configuration> from XML2 to <Configurations> in XML1 if no such matching <Domain> was found, for example:

var xdoc1 = XDocument.Load("path/to/xml1.xml");
var xdoc2 = XDocument.Load("path/to/xml2.xml");

foreach (var item in xdoc2.Descendants("Configuration"))
{
    var domain = (string)item.Element("Domain");

    // try to find <Components> with matching <Domain> in XML1
    var parent = xdoc1.Descendants("Configuration")
                        .Where(o => (string)o.Element("Domain") == domain)
                        .Elements("Components")
                        .FirstOrDefault();

    // if such <Components> is found, add <Component> from XML2 to it
    if(parent != null) 
    {
        parent.Add(item.Elements("Components").Elements("Component"));
    }
    // otherwise add <Configuration> from XML2 to <Configurations> in XML1 
    else 
    {
        xdoc1.Root.Element("Configurations").Add(item);
    }
}

// don't forget to save the modified XML1 if you need to
// here we only print the merged XML1
System.Console.WriteLine(xdoc1.ToString());

dotnetfiddle demo

Upvotes: 2

Related Questions