multi Elements to multi rows - c#

I made some try with XML Reader, Xpath... and know linq
But wont find a way to solve these things.
I have to extract the information, for each Order into one row, in this row should be the Information of the first elements and the Items and the orders as well as the status of the Orders...
Is there a way to extract all these information to one row within one linq-query? Or do I have to build steps for this?
(Visualstudio 2010/2013 C# .Net 4)
<Account>
<Name>Name1</Name>
<InId>100</InId>
<CustomID>100000087</CustomID>
<ZipCode>zipcode</ZipCode>
<Items>
<Item>
<ItemID>700</ItemID>
<ItemName>Itemname1</ItemName>
<Orders>
<Order>
<IDIndex>1000</IDIndex>
<IDParam>T1</IDParam>
<Themes>
<Theme>
<Status>Alert</Status>
<Lastget>01.01.2015</Lastget>
</Theme>
</Themes>
</Order>
</Orders>
<Item>
<ItemID>800</ItemID>
<ItemName>Itemname2</ItemName>
<Orders>
<Order>
<IDIndex>5001</IDIndex>
<IDParam>T1</IDParam>
<Themes>
<Theme>
<Status>Alert1</Status>
<Lastget>01.01.2015</Lastget>
</Theme>
</Themes>
</Order>
<Order>
<IDIndex>5002</IDIndex>
<IDParam>T1</IDParam>
<Themes>
<Theme>
<Status>Alert1</Status>
<Lastget>01.01.2015</Lastget>
</Theme>
</Themes>
</Order>
<Order>
<IDIndex>5003</IDIndex>
<IDParam>T1</IDParam>
<Themes>
<Theme>
<Status>Alert2</Status>
<Lastget>01.01.2015</Lastget>
</Theme>
</Themes>
</Order>
</Orders>
</Item>
</Items>
</Account>

Following query will give you the required data:-
var result = xdoc.Root.Descendants("Item")
.Select(x => new
{
Name = (string)x.Document.Root.Element("Name"),
InId = (string)x.Document.Root.Element("InId"),
CustomID = (string)x.Document.Root.Element("CustomID"),
ItemID = (string)x.Element("ItemID"),
ItemName = (string)x.Element("ItemName"),
OrdersList = x.Descendants("Order")
.Select(y => new
{
IDIndex = (string)y.Element("IDIndex"),
IDParam = (string)y.Element("IDParam"),
ThemesList = y.Descendants("Theme")
.Select(z => new
{
Status = (string)z.Element("Status"),
Lastget = (string)z.Element("Lastget")
}).ToList()
}).ToList()
});
Please note that two lists will be created for 2 items, and for each item I am creating a list of orders and within each order list of themes.

Related

XML to Dataset : How does one access a complex type within a node?

The XML file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfCompany xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Company>
<CompanyID>Company A</CompanyID>
<CompanyName>Company A</CompanyName>
<CompanyRecID>1491</CompanyRecID>
<Contacts>
<Contact>
<City>Birmingham</City>
<Country>United States</Country>
<FirstName>Steven</FirstName>
</Contact>
<Contact>
<City>Birmingham</City>
<Country>United States</Country>
<FirstName>Natalie</FirstName>
</Contact>
</Contacts>
</Company>
<Company>
<CompanyID>Company B</CompanyID>
<CompanyName>Company B</CompanyName>
<CompanyRecID>1492</CompanyRecID>
<Contacts>
<Contact>
<City>Birmingham</City>
<Country/>
<FirstName>Greg</FirstName>
</Contact>
<Contact>
<City>Birmingham</City>
<Country/>
<FirstName>Robert</FirstName>
</Contact>
</Contacts>
</Company>
</ArrayOfCompany>
With two lines of code, I can take that XML file and pump it into a dataset:
var dsCustomer = new System.Data.DataSet("CustomerDataSet");
dsCustomer.ReadXml(System.IO.Path.Combine(currentAssemblyDirectoryName, "company.xml"));
.NET understands the schema. This is what it looks like when I use ds.WriteXmlSchema command:
Given a company name, how can use a LINQ query to return the contacts?
You can parse xml with LINQ to XML:
var name = "Company B";
var xdoc = XDocument.Load("company.xml");
var contacts = from company in xdoc.Descendants("Company")
where (string)company.Element("CompanyName") == name
from contact in company.Element("Contacts").Elements()
select new {
City = (string)contact.Element("City"),
Country = (string)contact.Element("Country"),
FirstName = (string)contact.Element("FirstName")
};
This will return collection of anonymous objects representing contacts of selected company. Each object will have properties for city, country and first name.

XML node parsing using C# linq

i have xml document like this:
<?xml version="1.0" encoding="utf-8" ?>
<demographics>
<country id="1" value="USA">
<state id ="1" value="California">
<city>Long Beach</city>
<city>Los Angeles</city>
<city>San Diego</city>
</state>
<state id ="2" value="Arizona">
<city>Tucson</city>
<city>Phoenix</city>
<city>Tempe</city>
</state>
</country>
<country id="2" value="Mexico">
<state id ="1" value="Baja California">
<city>Tijuana</city>
<city>Rosarito</city>
</state>
</country>
</demographics>
How to select everything starting from demographics node using XML linq queries
something like this:
var node=from c in xmldocument.Descendants("demographics") ??
XDocument xDoc = XDocument.Parse(xml);
var demographics = xDoc
.Descendants("country")
.Select(c => new
{
Country = c.Attribute("value").Value,
Id = c.Attribute("id").Value,
States = c.Descendants("state")
.Select(s => new
{
State = s.Attribute("value").Value,
Id = s.Attribute("id").Value,
Cities = s.Descendants("city").Select(x => x.Value).ToList()
})
.ToList()
})
.ToList();

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>

LINQ to XML (Dynamic XML)

I have an XML file which has kind of a similar structure that you can see below:
I would like to select title and subitems using LINQ to XML. The difficulties that I have: sometimes a subitem can be just one and sometimes it can be 20 subitems, and I need to add them to List<string>.
<?xml version="1.0"?>
<items>
<item>
<title>Name of the title</title>
<subitem>Test</subitem>
<subitem1>Test</subitem1>
<subitem2>Test</subitem2>
<subitem3>Test</subitem3>
<subitem4>Test</subitem4>
<subitem5>Test</subitem5>
</item>
<item>
<title>Name of the title</title>
<subitem>Test</subitem>
<subitem1>Test</subitem1>
<subitem2>Test</subitem2>
<subitem3>Test</subitem3>
</item>
<item>
<title>Name of the title</title>
<subitem>Test</subitem>
<subitem1>Test</subitem1>
</item>
</items>
The solution, including getting the titles, is:
XDocument yourXDocument = XDocument.Load(yourXmlFilePath);
IEnumerable<Tuple<XElement, IEnumerable<XElement>>> yourSubItems =
yourXDocument.Root.Descendants()
.Where(xelem => xelem.Name == "title")
.Select(xelem => new Tuple<XElement, IEnumerable<XElement>>(xelem, xelem.Parent.Elements().Where(subelem => subelem.Name.LocalName.StartsWith("subitem")));
XDocument xdoc = XDocument.Load(path_to_xml);
var query = from i in xdoc.Descendants("item")
select new
{
Title = (string)i.Element("title"),
Subitems = i.Elements()
.Where(e => e.Name.LocalName.StartsWith("subitem"))
.Select(e => (string)e)
.ToList()
};

Categories