So say I have this XML file:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root>
<Category Name="Tasties">
<Category Name="Pasta">
<Category Name="Chicken">
<Recipe Name="Chicken and Shrimp Scampi" />
<Recipe Name="Chicken Fettucini Alfredo" />
</Category>
<Category Name="Beef">
<Recipe Name="Spaghetti and Meatballs" />
<Recipe Name="Lasagna" />
</Category>
<Category Name="Pork">
<Recipe Name="Lasagna" />
</Category>
<Category Name="Seafood">
<Recipe Name="Chicken and Shrimp Scampi" />
</Category>
</Category>
</Category>
</Root>
And I want to return the names of all the recipes in Tasties\Pasta\Chicken, how would I do this?
What I have currently is:
var q = from chk in
(from c in doc.Descendants("Category")
where c.Attribute("Name").Value == "Chicken"
select c)
select from r in chk.Descendants("Recipe")
select r.Attribute("Name").Value;
foreach (var recipes in q)
{
foreach (var recipe in recipes)
{
Console.WriteLine("Recipe name = {0}", recipe);
}
}
Which kinda works, although it doesn’t check the path, only for the first category named Chicken. I could dig through each element in the path recursively, but it seems like there probably is a better solution I’m missing. Also my current query returns IEnumerable<IEnumerable<String>> when all I want is just an IEnumerable<String>.
Basically I can make it work but it looks messy and I’d like to see any LINQ suggestions or techniques to do better querying.
Personally, I’d use
XmlDocumentand the familiarSelectNodes:For LINQ-to-XML, I’d guess (untested) something like:
Edit: if you want the category selection to be more flexible:
This should now work for any number of levels, selecting all recipes at the chosen level or below.
Edit for discussion (comments) on why
Wherehas a localtmpvariable:This might get a bit complex, but I’m trying to do the question justice ;-p
Basically, the
foreach(with the iterator lvalue “captured”) looks like:It might not be obvious, but since
Whereisn’t evaluated immediately, the value ofcategory(via the predicateAnonMethod) isn’t checked until much later. This is an unfortunate consequence of the precise details of the C# spec. Introducingtmp(scoped inside the foreach) means that the capture happens per iteration:And hence it doesn’t matter whether we evaluate now or later. Complex and messy. You can see why I favor a change to the specification!!!