Merge two XML files, files having one to many relationship C# - 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!

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.

Related

Get and Set XML elements with Namespaces

NEWBIE QUESTION.
I haven't worked that much with xml, nothing like this anyway. I have some XML as shown below that I receive which has several namespaces.
I need to read some values, then update others before returning the revised XML with namespaces intact - don't want them removed.
I am given the path to some of the elements like this cred/sub/aa or trip/items/item[0]/customerInfo/custName.
But it seems that namespaces make it difficult to get to those elements so simply.
Does anybody know how I can read some of the values like NON-SMOKING from custPref or get the value CABBAGE from bossman/zz.
Also, I want to be able to then set a value such as custName to say Mr. X.
Any ideas?
Thanks.
<?xml version="1.0" encoding="utf-16" ?>
<A1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<cred xmlns="https://blah-blah.com/?foobar">
<sub>
<aa>Zippo</aa>
<bb>lighter</bb>
</sub>
<reqId>
<cc></cc>
<dateOfBirth></dateOfBirth>
</reqId>
</cred>
<reqName xmlns="http://blah-blah/vader/base">qwerty</reqName>
<reqId xmlns="http://blah-blah/vader/base">12345</reqId>
<machine xmlns="http://blah-blah/vader/base">
<qqq>hello</qqq>
<www>goodbye</www>
<eee>99999</eee>
<rrr>88888</rrr>
</machine>
<monkey xmlns="http://blah-blah/vader/base">alskdjfhg</monkey>
<math xmlns="http://blah-blah/vader/base">
<language></language>
</math>
<trip xmlns="http://blah-blah/simple">
<tripOverview xmlns="http://blah-blah/vader/base">
<description></description>
<cost></cost>
</tripOverview>
<bossman xmlns="http://blah-blah/vader/base">
<zz>CABBAGE</zz>
<yy>BANANA</yy>
<xx>MELON</xx>
<ww>SYRUP</ww>
</bossman>
<items>
<item>
<itemSummary xmlns="http://blah-blah/vader/base">
<description></description>
<cost></cost>
<reference></reference>
</itemSummary>
<customerInfo xmlns="http://blah-blah/vader/base">
<custName></custName>
<custPref>NON-SMOKING</custPref>
</customerInfo>
<seatId xmlns="http://blah-blah/vader/base">1</seatId>
</item>
</items>
</trip>
</A1>
string xml = "<Root><Options></Options></Root>";
var xdocs = XDocument.Parse(xml);
xdocs.Descendants().Where(q => q.Name == "Options").FirstOrDefault().Value = "FoundIt";

How to insert a child element into an existing element

I am using a XDocument to write an xml file and I am writing to the document in two different places. After the first write I have
<?xml version="1.0" encoding="utf-8"?>
<suspensedata connectionid="000" customerid="000" name="MyName" />
After the second write I want the file to look like this
<?xml version="1.0" encoding="utf-8"?>
<suspensedata connectionid="560" customerid="131" name="ImgTransfer2327">
<transaction DocumentID="46" SuspenseID="7">
<field id="LocationID">000000015000</field>
<field id="AccountNumber">50000</field>
<field id="AmountPaid">25.00</field>
<field id="CheckAmount">100.00</field>
<field id="CheckNo">000</field>
</transaction>
</suspensedata>
But I can't seem to get the insert done correctly.
I've tried (The name of my XDocument is ValidXml) ValidXml.Root.Add(new Element("transaction"));
and that does not change anything.
I have also tried ValidXml.Element("suspensedata").Add(new XElement("transaction"));
But that did not work either.
How would I add this child element?
EDIT: Both attempts did not produce any other output besides the output on the first try. Also I did make sure to use ValidXml.Save()
I have tried something like this I supposed that the two parts are in 2 files
//xmlfile1 contains the first part
<?xml version="1.0" encoding="utf-8"?>
<suspensedata connectionid="000" customerid="000" name="MyName" />
//this part will be loaded like this
XDocument xDoc = XDocument.Load("xmlfile1.xml");
XElement elt = xDoc.Root;
//and the second file contains the second part
XDocument xDoc2 = XDocument.Load("xmlfile2.xml");
XElement elt2 = xDoc2.Root;
elt.Add(elt2);
xDoc.Save("xmlfile1.xml");
Hope this help

Adding an XML file to your XNA project

I'm creating an XNA game. I've made it so I can specify all the level details in an XML file which is then de-serialized and used to set up the level details.
At the moment, it's just referencing a file on my computer - my question is, how do I reference this more generically?
Adding the xml in my content folder created a multitude of complaints about schemas and such like, which made me think that likely wasn't the correct route.
Any suggestions?
I tried removing all the entries from the XNA, this gives:
Attempt to access the method failed: System.IO.StreamReader..ctor(System.String)
EDIT:
The xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XnaContent>
<Asset Type = "RDrop.Level[]">
<Item>
(stuff)
</Item>
<Item>
(stuff)
</Item>
</Asset>
</XnaContent>
EDIT:
I've started a new windows phone project - the previous one wasn't one. I've copied everything over and added "dataTypes" ala this tutorial:
http://msdn.microsoft.com/en-us/library/ff604979.aspx
Game project references -> content, MyDataTypes.
Content references -> MyDataTypes.
The XML is as is in previous edit and is contained in the content folder via Add-> Existing Item-> Level.XML.
Any ideas?
You can leave the build action as "Compile". One method to do what you want is the following:
Create a class that the xml is going to be describing. Example: Level.cs
Then structure your xml file like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XnaContent>
<Asset Type="The_Level_class_namespace.Level">
<Property1>Value</Property1>
<Property2>Value</Property2>
<Property3>Value</Property3>
<Property4>Value</Property4>
</Asset>
</XnaContent>
if you want the xml to describe an array of objects you can do structure the xml like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XnaContent>
<Asset Type="The_Level_class_namespace.Level[]">
<Item>
<Property1>Value</Property1>
<Property2>Value</Property2>
<Property3>Value</Property3>
<Property4>Value</Property4>
</Item>
</Asset>
</XnaContent>
From there you just need to make sure your values are in the proper format. For example a vector2 object would be like this:
<Vector2Property>x_value y_value</Vector2Property>
Make sure that your content project references the game project or library project.
Hope this helps :)
Open the properties of your XML document (right click in your content folder). You can set the Build Action to : None.
That way, the compiler won't analyse your schema, thus it won't produce any warnings.
(I'm not entirely sure about this, just my first guess)

C# XML Merging (2+ files)

I am having problem to merge 2 or more xml files into 1 using c#.
I am doing it with DataSets:
//ds1,ds2,ds3 are DataSets
private void MyMethod()
{
ds1.ReadXml(tmpStream);
ds2.ReadXml(tmpStream);
ds1.Merge(ds2);
}
but i dont want to use DataSet. i am searching for another way.
first XML is
<?xml version="1.0" encoding="utf-8"?>
<catalog>
<item>
<path>'filePath'</path>
<deleted>0</deleted>
<date>9/23/2010 11:30:03 AM</date>
</item>
</catalog>
the second is
<?xml version="1.0" encoding="utf-8"?>
<catalog>
<item>
<path>'filePath'</path>
<deleted>0</deleted>
<date>9/23/2010 11:30:03 AM</date>
</item>
</catalog>
result must be
<?xml version="1.0" encoding="utf-8"?>
<catalog>
<item>
<path>'filePath'</path>
<deleted>0</deleted>
<date>9/23/2010 11:30:03 AM</date>
</item>
<item>
<path>'filePath'</path>
<deleted>0</deleted>
<date>9/23/2010 11:30:03 AM</date>
</item>
</catalog>
Though this isn't really clear of what sort of merge you want, this article Merging XML Files, Schema Validation, and More might help you get the idea.
Easiest could be, if you dont want any checks to be performed(duplicates, zombies, etc)
var ResultXml = XDocument.Load("file1.xml");
ResultXml.Root.Add(XDocument.Load("file2.xml").Root.Elements());
To merge XML files into resulting one, you could use Microsoft's XML Diff and Patch C# API. You could read more about it in Eric White's blog post: "OpenXmlDiff.Exe: A Utility to Find the Differences Between Two Open XML Documents"

How to read nested XML using xDocument in Silver light?

Hi currently I have a nested XMl , having the following Structure :
<?xml version="1.0" encoding="utf-8" ?>
<Response>
<Result>
<item id="something" />
<price na="something" />
<?xml version="1.0" encoding="UTF-8" ?>
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">
</Result>
<NumberReturned>10</NumberReturned>
<TotalMatches>10</TotalMatches>
</Response>
Any help on how to read this using Xdocument or XMLReader will be really helpfull.
Thanks,
Subhendu
XDocument and XmlReader are both XML parsers that expect a properly formed XML as input. What you have shown is not a XML file. So the first task would be to extract the nested XML and as this is not valid XML you cannot rely on any parser to do this job. You'll need to resort to string manipulation and or regular expressions.
My suggestion would be to fix the procedure generating this invalid XML in the first place. Another suggestion is to never generate a XML file manually but use an appropriate tool for this (XmlWriter, XDocument, ...)

Categories