Merge two XML files, files having one to many relationship C#

I have two XML files, 1. Contracts

<?xml version="1.0" encoding="UTF-8"?>
<File>
   <Contract>
      <ContractNo>1</ContractNo>
   </Contract>
   <Contract>
      <ContractNo>2</ContractNo>
   </Contract>
</File>

2. Asset

<?xml version="1.0" encoding="UTF-8"?>
<File>
   <Asset>
      <ContractNo>1</ContractNo>
      <SomeData>XXXX</SomeData>
      <SomeData2>XXXX</SomeData2>
   </Asset>
   <Asset>
      <ContractNo>1</ContractNo>
      <SomeData>YYYY</SomeData>
      <SomeData2>YYYY</SomeData2>
   </Asset>
   <Asset>
      <ContractNo>2</ContractNo>
      <SomeData>ZZZZ</SomeData>
      <SomeData>ZZZZ</SomeData>
   </Asset>
</File>

A contract may have one or more assets. XML files are mapped by the contract number. I'm going to merge these two files and create the below xml

<?xml version="1.0" encoding="UTF-8"?>
<File>
   <Contract>
      <ContractNo>1</ContractNo>
      <Asset>
         <SomeData>XXXX</SomeData>
         <SomeData2>XXXX</SomeData2>
      </Asset>
      <Asset>
         <SomeData>YYYY</SomeData>
         <SomeData2>YYYY</SomeData2>
      </Asset>
   </Contract>
   <Contract>
      <ContractNo>2</ContractNo>
      <Asset>
         <SomeData>ZZZZ</SomeData>
         <SomeData2>ZZZZ</SomeData2>
      </Asset>
   </Contract>
</File>

My approach is to iterate each contract of contract xml and find the contract number, then iterate assets xml and find the asset nodes of the above contract and merge them to the contract xml

XmlNodeList contractsNodeList = contractsDocument.GetElementsByTagName("Contract");
string contractNumber;
foreach (XmlNode contractNode in contractsNodeList)
{
    //get the contract number
    contractNumber = contractNode.SelectSingleNode("ContractNo").InnerText;

    if (!String.IsNullOrEmpty(contractNumber))
    {
        XmlNodeList assetsNodeList = assetsDocument.GetElementsByTagName("Asset");
        foreach (XmlNode assetNode in assetsNodeList)
        {
            //checking whether the current asset node has the current contract number 
            if (assetNode.ChildNodes[0].InnerText == contractNumber)
            {
                //remove the contract number of the asset node
                assetNode.RemoveChild(assetNode.ChildNodes[0]);
                //append the asset element to the contract xml
                contractNode.AppendChild(contractNode.OwnerDocument.ImportNode(assetNode, true));
            }
        }
    }
}

This code works and generates the required xml. But it not much efficient. I don't have much experiance in working with XML. Please let me know any other ways to do this. Thank you!

Jon Skeet
people
quotationmark

I would personally read in the assets, populating an ILookup<int, XElement> and removing the ContractNo element afterwards (as it's just slightly simpler in LINQ to XML). Then read the contracts, populating the assets from the dictionary. Something like:

XDocument assets = XDocument.Load("assets.xml");
var lookup = assets.Root.Elements("Asset")
                        .ToLookup(x => (int) x.Element("ContractNo"));
assets.Root.Elements("Asset").Elements("ContractNo").Remove();

XDocument contracts = XDocument.Load("contracts.xml");
foreach (var contract in contracts.Root.Elements("Contract").ToList())
{
    var id = (int) contract.Element("ContractNo");
    contract.Add(lookup[id]);
}
contracts.Save("results.xml");

Note that this doesn't detect contracts that don't have any assets - they'll just be left as they are.

All of this is doable in the "old" XmlDocument API, but LINQ to XML does tend to make it much simpler.

people

See more on this question at Stackoverflow