generate XML with nested parameter in c#.net - c#

I want to generate an XML with parameter by parsing the following xml. i am working in c#.net.
<root>
<name1>
<names>
<id>5</id>
<class>space</class>
<from>Germany</from>
<to>France</to>
<through>
<via>
<id>4</id>
<route>Zurich<route>
</via>
<via>
<id>7</id>
<route>Vienna<route>
</via>
</through>
</names>
</name1>
<name2>
<newNames>
<id>8</id>
<path>Road</path>
<dest>USA</dest>
<through>
<route1>
<id>5</id>
<naviagte>Britain</naviagte>
</route1>
<route1>
<id>2</id>
<naviagte>Canada</naviagte>
</route1>
</through>
</newNames>
</name2>
</root>
i want to convert it like following-
<root>
<name1>
<names id = "5";class = "space"; from = "Germany" ; to = "France">
<through>
<via id = "4" ; route = "Zurich">
<via id = "7" ; route = "Vienna">
</through>
</names>
</name1>
<name2>
<newNames id = "8"; path = "Road"; dest = "USA">
<newNames id = "8"; path = "Road"; dest = "USA">
<through>
<route1 = id = "5" ; naviagte = "Britain">
<route1 = id = "2" ; naviagte = "Canada">
</through>
</name2>
</root>
i have tried the following codes.
var doc = XDocument.Load("xml_file.xml");
Console.WriteLine(doc.ToString());
var names = doc.Descendants("name");
var newRootElement = new XElement("root");
foreach (var name in names)
{
var newNameElement = new XElement(name.Name);
foreach (var element in name.Elements())
{
newNameElement.SetAttributeValue(element.Name, element.Value);
}
newRootElement.Add(newNameElement);
}
Console.WriteLine(newRootElement.ToString());
newRootElement.Save("converted_xml_file.xml");
but i can't parse all the nodes. could anyone give me any hints or correction in my codes please ?

I suppose you have closed route tags in your input xml. If so, then you can build new xml by querying original xml and replacing elements with attributes:
var xdoc = XDocument.Load("xml_file.xml");
var root =
new XElement("root",
from name in xdoc.Root.Elements()
select new XElement(name.Name,
from names in name.Elements()
select new XElement(names.Name,
from namesElement in names.Elements()
where namesElement.Name.LocalName != "through"
select new XAttribute(namesElement.Name.LocalName, (string)namesElement),
new XElement("through",
from route in names.Element("through").Elements()
select new XElement(route.Name,
from routeElement in route.Elements()
select new XAttribute(routeElement.Name.LocalName, (string)routeElement))))));
This code produces following xml:
<root>
<name1>
<names id="5" class="space" from="Germany" to="France">
<through>
<via id="4" route="Zurich" />
<via id="7" route="Vienna" />
</through>
</names>
</name1>
<name2>
<newNames id="8" path="Road" dest="USA">
<through>
<route1 id="5" naviagte="Britain" />
<route1 id="2" naviagte="Canada" />
</through>
</newNames>
</name2>
</root>

Related

Descendants not found even if they exist in XDocument in C#

I am having problems with getting descendant with specific name. I have hugh XML that basically is made of lots of this elements:
<?xml version="1.0" encoding="utf-8"?>
<Search_Results xmlns="https://support.bridgerinsight.lexisnexis.com/downloads/xsd/4.5/OutputFile.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://support.bridgerinsight.lexisnexis.com/downloads/xsd/4.5/OutputFile.xsd https://support.bridgerinsight.lexisnexis.com/downloads/xsd/4.5/OutputFile.xsd">
<Entity Record="28" ResultID="12460985">
<GeneralInfo>
<EntityType>Individual</EntityType>
<Name>Jón Jónsson</Name>
<DOB>01/01/0001</DOB>
<DOBParsed />
<AccountID>ABS-ASSOC-10-109</AccountID>
<IDLabel>Account ID</IDLabel>
<IDNumber>ABS-ASSOC-10-109</IDNumber>
<AddressType>Current</AddressType>
<PostalCode>Somalia</PostalCode>
</GeneralInfo>
<RecordDetailInfo>
<EntityType>Individual</EntityType>
<SearchDate>2016-05-13 09:53:50Z</SearchDate>
<Origin>Automatic Batch</Origin>
<FirstName>Jón</FirstName>
<LastName>Jónsson</LastName>
<FullName>Jón Jónsson</FullName>
<AdditionalInfo>
<Type>Date of Birth</Type>
<Information>01/01/0001</Information>
</AdditionalInfo>
<Addresses>
<Type>Current</Type>
<PostalCode>Somalia</PostalCode>
</Addresses>
<Identifications>
<Type>Account ID</Type>
<Number>ABS10-109</Number>
</Identifications>
</RecordDetailInfo>
<WatchList>
<Match ID="1">
<EntityName>Jonsson</EntityName>
<EntityScore>96</EntityScore>
<BestName>Jonsson, Jon Orn</BestName>
<BestNameScore>96</BestNameScore>
<FileName>WorldCompliance - Full.BDF</FileName>
<SourceDate>2016-05-11 05:01:00Z</SourceDate>
<DistributionDate>2016-05-12 14:59:39Z</DistributionDate>
<ResultDate>2016-05-13 09:53:50Z</ResultDate>
<EntityUniqueID>WX0003219444</EntityUniqueID>
<MatchDetails>
<Entity Type="2">
<Number>3219444</Number>
<Date>9/3/2012</Date>
<Reason>International</Reason>
<CheckSum>69185</CheckSum>
<Gender>Male</Gender>
<Name>
<First>Jon Orn</First>
<Last>Jonsson</Last>
<Full>Jon Orn Jonsson</Full>
</Name>
<Notes>Source.</Notes>
<Addresses>
<Address ID="1" Type="4">
<Country>Iceland</Country>
</Address>
</Addresses>
<IDs>
<ID ID="1" Type="27">
<Number>3219444</Number>
</ID>
</IDs>
<Descriptions>
<Description ID="1" Type="10">
<Value>Honorary Consul of Iceland in Saskatchewan, Canada</Value>
<Notes>Starting 2002 Ending 2014</Notes>
</Description>
<Description ID="2" Type="22">
<Value>Link to WorldCompliance Online Database</Value>
<Notes>Jonsson, Jon Orn | https://members.worldcompliance.com/metawatch2.aspx?id=e0399c29-7c5e-4674-874c-f36fdb19052e</Notes>
</Description>
<Description ID="3" Type="22">
<Value>Sources of Record Information</Value>
<Notes>http://brunnur.mfa.is/interpro/utanr/HBvefur.nsf/Pages/IslSendiradIsl?OpenDocument&amp | CountryNr=1(Canada)&amp | Lang=44') | http://www.international.gc.ca/protocol-protocole/assets/pdfs/Diplomatic_List.pdf | http://www.inlofna.org/Elfros/newsletter%20January%202010.pdf | http://publications.gc.ca/collections/Collection/E12-3-2002E.pdf | http://www.onlygolfnews.com/golf-canada-saskatchewan/saskatchewan-golf-first-fort-lacrosse-ted-brandon-over-new-last-snow.htm | http://www.ops.gov.sk.ca/Consular-Officers</Notes>
</Description>
</Descriptions>
</Entity>
</MatchDetails>
</Match>
</WatchList>
</Entity>
</Search_Results>
I am trying to reach all elements with name: Entity and later I want to go through all of them and get values from their descendants with name "Reason".
But non of the Entity elements is found with this line:
var entityList = xmlDoc.Descendants(nameSpace + "Entity").ToList();
This is a whole method I am using:
public static void GetIBANAndBicValuesFromXML(XDocument xmlDoc)
{
var reasons = new List<string>();
XNamespace nameSpace =
"https://support.bridgerinsight.lexisnexis.com/downloads/xsd/4.5/";
var entityList = xmlDoc.Descendants(nameSpace + "Entity").ToList();
if (entityList != null)
{
foreach (var reason in entityList.Select(entity => entity.Elements(nameSpace + "Reason"))
.Where(reasonsList => reasonsList != null).SelectMany(reasonsList => reasonsList))
{
string reasonValue = reason.Value;
reasons.Add(reasonValue);
}
}
}
And this is a call to this method:
private static void Main(string[] args)
{
var xmlFile = "C:\\temp\\indi2.xml";
var x = XDocument.Load("C:\\temp\\Individuals.xml");
XMLParse.GetIBANAndBicValuesFromXML(x);
}
I have tried namespace like this as well:
"https://support.bridgerinsight.lexisnexis.com/downloads/xsd/4.5/OutputFile.xsd"
But no success.
Anybody sees what I am doing wrongly?
You can use Linq to filter with LocalName:
string fileName = "1.txt";
var xDoc = XDocument.Load(fileName);
var neededElements = xDoc.Descendants().Where(x => x.Name.LocalName == "Entity");
Console.WriteLine("Found {0} Entitys", neededElements.Count());
foreach(var el in neededElements)
{
Console.WriteLine(el);
}

How to turn one XML record with sub nodes into multiple records without XSLT or SQL

I need to figure out how to turn one XML record with sub nodes into multiple records using C#. Yes, I know it would be easier to do this using XSLT but that isn't an option. I have an XML file that must be modified to be used by 5 different uses so I need a common starting point.
Forgive my lack of understanding but nothing I can find seems to go in this direction. Everything goes in the other direction. Here is a sample of my code and file.
The source file.
<inventoryitems>
<inventoryitem>
<id>11101</id>
<displayname>LG HAMBURGER PATTY</displayname>
<basemeasure>EACH</basemeasure>
<reportingmeasure>EACH</reportingmeasure>
<measures>
<measure>
<name>CS</name>
<factor>1.000000</factor>
<isactive>1</isactive>
</measure>
<measure>
<name>ST</name>
<factor>8.000000</factor>
<isactive>1</isactive>
</measure>
<measure>
<name>EACH</name>
<factor>120.000000</factor>
<isactive>1</isactive>
</measure>
</measures>
<categories>
<category>
<name>MEATS</name>
</category>
</categories>
<locations />
<skus />
</inventoryitem>
<inventoryitem>
<id>11102</id>
<displayname>SM HAMBURGER PATTY</displayname>
<basemeasure>EACH</basemeasure>
<reportingmeasure>EACH</reportingmeasure>
<measures>
<measure>
<name>ST</name>
<factor>6.000000</factor>
<isactive>1</isactive>
</measure>
<measure>
<name>CS</name>
<factor>1.000000</factor>
<isactive>1</isactive>
</measure>
<measure>
<name>EACH</name>
<factor>96.000000</factor>
<isactive>1</isactive>
</measure>
</measures>
<categories>
<category>
<name>MEATS</name>
</category>
</categories>
<locations />
<skus />
</inventoryitem>
<inventoryitem>
<id>11202</id>
<displayname>BREAD SM BUN 4</displayname>
<basemeasure>EACH</basemeasure>
<reportingmeasure>EACH</reportingmeasure>
<measures>
<measure>
<name>TR</name>
<factor>1.000000</factor>
<isactive>1</isactive>
</measure>
<measure>
<name>EACH</name>
<factor>30.000000</factor>
<isactive>1</isactive>
</measure>
</measures>
<categories>
<category>
<name>BAKERY</name>
</category>
</categories>
<locations />
<skus />
</inventoryitem>
</inventoryitems>
What I need to get would look something like this.
<data>
<row InventoryItemId="11201" ItemDescription="BREAD LG BUN 5" CategoryName="BAKERY" Measure="TR" />
<row InventoryItemId="11201" ItemDescription="BREAD LG BUN 5" CategoryName="BAKERY" Measure="EACH" />
</data>
I was able to write the code to mode the value to an attribute when the is only one node but I am at a loss on what to do when there is a sub node with multiple values.
invlist = results.Substring(results.IndexOf("<inventoryitems>"), (results.IndexOf("</inventoryitemsresponsedata>") - results.IndexOf("<inventoryitems>")));
XmlDocument doc = new XmlDocument();
XmlNode nd = doc.CreateNode("element", "data", "");
doc.AppendChild(nd);
//XmlNode rw = doc.CreateNode("element", "row", "");
//nd.AppendChild(rw);
var invitems = new XmlDocument { InnerXml = invlist };
XmlNode result = doc.ImportNode(invitems.DocumentElement, true);
nd.AppendChild(result);
XmlNodeList ndList = doc.SelectNodes("data/inventoryitems/inventoryitem");
foreach (XmlNode id in ndList)
{
XmlNode idnode = id.SelectSingleNode("id");
if (idnode != null)
{
XmlNode rw = doc.CreateNode("element", "row", "");
nd.AppendChild(rw);
var attribute = doc.CreateAttribute("InventoryItemId");
attribute.Value = idnode.InnerXml;
var Description = doc.CreateAttribute("ItemDescription");
Description.Value = id.SelectSingleNode("displayname").InnerXml;
rw.Attributes.Append(attribute);
rw.Attributes.Append(Description);
}
XmlNodeList msList = id.SelectNodes("measures/measure");
foreach (XmlNode mes in msList)
{
XmlNode msnode = mes.SelectSingleNode("name");
if (msnode != null)
{
var attribute = doc.CreateAttribute("Measure");
attribute.Value = msnode.InnerXml;
//rw.Attributes.Append(attribute);
mes.Attributes.Append(attribute);
}
}
}
Any help would be appreciated.
Update: This is what I am getting.
<data>
<row InventoryItemId="11201" ItemDescription="BREAD LG BUN 5" CategoryName="BAKERY" />
<inventoryitem>
<measures>
<measure Measure="TR"></measure>
<measure Measure="EACH"></measure>
</measures>
<locations />
<skus />
</inventoryitem>
</data>
I figured it out. By wrapping each node inside a row node loop I get the results I was looking for.
var item = idnode.InnerXml;
XmlNodeList rwList = doc.SelectNodes(String.Format("data/row[#InventoryItemId='{0}']",item));
var rwCount = rwList.Count;
foreach (XmlNode rw in rwList)
{
XmlNodeList msList = id.SelectNodes("measures/measure");
foreach (XmlNode mes in msList)
{
XmlNode msnode = mes.SelectSingleNode("name");
{
var attribute = doc.CreateAttribute("Measure");
attribute.Value = msnode.InnerXml;
if (rwCount > 0)
{
rw.Attributes.Append(attribute);
rwCount--;
}
else
{
XmlNode clonenode = rw.Clone();
clonenode.Attributes.Append(attribute);
nd.AppendChild(clonenode);
}
}
}
}

Adding data to XML file

My XML file is:
<?xml version="1.0" encoding="utf-8" ?>
<people>
<person
index="1"
name="Zlecenie numer jeden"
beneficiary="Kowalski"
description="Proste zlecenie jakiejs strony czy cos"
price="800"
deadline="27.12.2013" />
</people>
How can I add to this existing file, something like new record:
<person
index="4"
name="Zlecenie numer cztery"
beneficiary="Kowalski"
description="Proste zlecenie jakiejs strony czy cos"
price="800"
deadline="27.12.2013" />
or remove or if you know how to update existing record then this too. Thanks
Try the next code snippet for adding element into xml. Note that I've used xml as string with escaped characters. You probably have xml file
var str = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<people>\r\n<person\r\nindex=\"1\"\r\nname=\"Zlec" +
"enie numer jeden\"\r\nbeneficiary=\"Kowalski\"\r\ndescription=\"Proste zlecenie jakiejs " +
"strony czy cos\"\r\nprice=\"800\"\r\ndeadline=\"27.12.2013\" />\r\n</people>";
var xml = XElement.Parse(str);
var newNode = new XElement("person",
new XAttribute("index", 4),
new XAttribute("name", "Zlecenie numer cztery"),
new XAttribute("beneficiary", "Kowalski"),
new XAttribute("description", "Proste zlecenie jakiejs strony czy cos"),
new XAttribute("price", 800),
new XAttribute("deadline", "27.12.2013"));
xml.Add(newNode);
//you can store whole xml tree in one variable simply by calling ToString on xml
str = xml.Tostring();
Console.WriteLine(str);
Prints:
<people>
<person index="1" name="Zlecenie numer jeden" beneficiary="Kowalski" description="Proste zlecenie jakiejs strony czy cos" price="800" deadline="27.12.2013" />
<person index="4" name="Zlecenie numer cztery" beneficiary="Kowalski" description="Proste zlecenie jakiejs strony czy cos" price="800" deadline="27.12.2013" />
</people>
XDocument xdoc = XDocument.Load(xmlFileName);
void Update(XDocument xdoc )
{
XElement repser = doc.Root.Element("people").Elements("person").Where(r => (int)r.Attribute("index") = xdoc.index).FirstOrDefault();
if (repser != null)
{
// update
repser.SetAttribute("name", xdoc.name);
repser.SetAttribute("beneficiary", xdoc.beneficiary);
repser.SetAttribute("description", xdoc.description);
repser.SetAttribute("price", xdoc.price);
repser.SetAttribute("deadline", xdoc.deadline);
// and so on
}
else
{
//insert
doc.Root.Element("people").Add
new XElement("person",
new XAttribute("index", xdoc.id),
new XAttribute("name", xdoc.name),
new XAttribute("beneficiary", xdoc.beneficiary),
new XAttribute("description", xdoc.description),
new XAttribute("price", xdoc.price),
new XAttribute("deadline", xdoc.deadline)
// and so on
));
}
}
You can manually plug in the values in the XAttribute values in the else statement for the update.

Xml simplification/extraction of distinct values - possible LINQ

Sorry for this long post....But i have a headache from this task.
I have a mile long xml document where I need to extract a list, use distinct values, and pass for transformation to web.
I have completed the task using xslt and keys, but the effort is forcing the server to its knees.
Description:
hundreds of products in xml, all with a number of named and Id'ed cattegories, all categories with at least one subcategory with name and id.
The categories are unique with ID, all subcategories are unique WITHIN that category:
Simplified example form the huge file (left our tons of info irrelevant to the task):
<?xml version="1.0" encoding="utf-8"?>
<root>
<productlist>
<product id="1">
<name>Some Product</name>
<categorylist>
<category id="1">
<name>cat1</name>
<subcategories>
<subcat id="1">
<name>subcat1</name>
</subcat>
<subcat id="2">
<name>subcat1</name>
</subcat>
</subcategories>
</category>
<category id="2">
<name>cat1</name>
<subcategories>
<subcat id="1">
<name>subcat1</name>
</subcat>
</subcategories>
</category>
<category id="3">
<name>cat1</name>
<subcategories>
<subcat id="1">
<name>subcat1</name>
</subcat>
</subcategories>
</category>
</categorylist>
</product>
<product id="2">
<name>Some Product</name>
<categorylist>
<category id="1">
<name>cat1</name>
<subcategories>
<subcat id="2">
<name>subcat2</name>
</subcat>
<subcat id="4">
<name>subcat4</name>
</subcat>
</subcategories>
</category>
<category id="2">
<name>cat2</name>
<subcategories>
<subcat id="1">
<name>subcat1</name>
</subcat>
</subcategories>
</category>
<category id="3">
<name>cat3</name>
<subcategories>
<subcat id="1">
<name>subcat1</name>
</subcat>
</subcategories>
</category>
</categorylist>
</product>
</productlist>
</root>
DESIRED RESULT:
<?xml version="1.0" encoding="utf-8"?>
<root>
<maincat id="1">
<name>cat1</name>
<subcat id="1"><name>subcat1</name></subcat>
<subcat id="2"><name>subcat2</name></subcat>
<subcat id="3"><name>subcat3</name></subcat>
</maincat>
<maincat id="2">
<name>cat2</name>
<subcat id="1"><name>differentsubcat1</name></subcat>
<subcat id="2"><name>differentsubcat2</name></subcat>
<subcat id="3"><name>differentsubcat3</name></subcat>
</maincat>
<maincat id="2">
<name>cat2</name>
<subcat id="1"><name>differentsubcat1</name></subcat>
<subcat id="2"><name>differentsubcat2</name></subcat>
<subcat id="3"><name>differentsubcat3</name></subcat>
</maincat>
</root>
(original will from 2000 products produce 10 categories with from 5 to 15 subcategories)
Things tried:
Xslt with keys - works fine, but pooooor performance
Played around with linq:
IEnumerable<XElement> mainCats =
from Category1 in doc.Descendants("product").Descendants("category") select Category1;
var cDoc = new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("root"));
cDoc.Root.Add(mainCats);
cachedCategoryDoc = cDoc.ToString();
Result was a "categories only" (not distinct values of categories or subcategories)
Applied the same xlst to that, and got fairly better performance..... but still far from usable...
Can i apply some sort of magic with the linq statement to have the desired output??
A truckload of good karma goes out to the ones that can point me in det right direction..
//Steen
NOTE:
I am not stuck on using linq/XDocument if anyone has better options
Currently on .net 3.5, can switch to 4 if needed
If I understood your question corectly, here's a LINQ atempt.
The query below parses your XML data and creates a custom type which represents a category and contains the subcategories of that element.
After parsing, the data is grouped by category Id to get distinct subcategories for each category.
var doc = XElement.Load("path to the file");
var results = doc.Descendants("category")
.Select(cat => new
{
Id = cat.Attribute("id").Value,
Name = cat.Descendants("name").First().Value,
Subcategories = cat.Descendants("subcat")
.Select(subcat => new
{
Id = subcat.Attribute("id").Value,
Name = subcat.Descendants("name").First().Value
})
})
.GroupBy(x=>x.Id)
.Select(g=>new
{
Id = g.Key,
Name = g.First().Name,
Subcategories = g.SelectMany(x=>x.Subcategories).Distinct()
});
From the results above you can create your document using the code below:
var cdoc = new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("root"));
cdoc.Root.Add(
results.Select(x=>
{
var element = new XElement("maincat", new XAttribute("id", x.Id));
element.Add(new XElement("name", x.Name));
element.Add(x.Subcategories.Select(c=>
{
var subcat = new XElement("subcat", new XAttribute("id", c.Id));
subcat.Add(new XElement("name", c.Name));
return subcat;
}).ToArray());
return element;
}));
Try this i have done something for it.. attributes are missing you can add them using XElement ctor
var doc = XDocument.Load(reader);
IEnumerable<XElement> mainCats =
doc.Descendants("product").Descendants("category").Select(r =>
new XElement("maincat", new XElement("name", r.Element("name").Value),
r.Descendants("subcat").Select(s => new XElement("subcat", new XElement("name", s.Element("name").Value)))));
var cDoc = new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("root"));
cDoc.Root.Add(mainCats);
var cachedCategoryDoc = cDoc.ToString();
Regards.
This will parse your xml into a dictionary of categories with all the distinct subcategory names. It uses XPath from this library: https://github.com/ChuckSavage/XmlLib/
XElement root = XElement.Load(file);
string[] cats = root.XGet("//category/name", string.Empty).Distinct().ToArray();
Dictionary<string, string[]> dict = new Dictionary<string, string[]>();
foreach (string cat in cats)
{
// Get all the categories by name and their subcat names
string[] subs = root
.XGet("//category[name={0}]/subcategories/subcat/name", string.Empty, cat)
.Distinct().ToArray();
dict.Add(cat, subs);
}
Or the parsing as one statement:
Dictionary<string, string[]> dict = root
.XGet("//category/name", string.Empty)
.Distinct()
.ToDictionary(cat => cat, cat => root
.XGet("//category[name={0}]/subcategories/subcat/name", string.Empty, cat)
.Distinct().ToArray());
I give you the task of assembling your resulting xml from the dictionary.

How retrieve deeper siblings using LINQ to XML?

I have an XML structure as follows. I need to extract "Value" and "String" by matching the command attributes? How to write LINQ for this?
<Root>
<Command val="1001" type="sync">
<Status>
<DataList>
<Info>
<Value>1</Value>
<String>Sample String 1 is set</String>
</Info>
<Info>
<Value>2</Value>
<String>Sample String 2 is set</String>
</Info>
<Info>
<Value>3</Value>
<String>Sample String 3 is set</String>
</Info>
</DataList>
</Status>
<Command>
</Root>
I tried something as below but exception occurred while running.
lst = (
from command in xmlDoc.Descendants("Command")
.Descendants("Status")
.Descendants("DataList")
select new EnumList
{
val = command.Element("Value").Value,
stringVal = command.Element("String").Value,
})
.ToList();
Try
lst = (
from command in xmlDoc.Descendants("Info")
select new EnumList
{
val = command.Element("Value").Value,
stringVal = command.Element("String").Value,
})
.ToList();
and you have error in xml sample (no close tag Command), change it to
</Command>
</Root>

Categories