Reputation: 1183
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
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
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());
Upvotes: 2