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!
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.
See more on this question at Stackoverflow