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.
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();
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.
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>
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()
};