How do I get all the subnodes of a node with certain attribute?

551 views Asked by At

I have an xml document that goes like this:

<Menu>
    <Category name="Comida Rapida">
        <Food cocina="si">
            <Name>Haburguesa</Name>
            <Price>10</Price>
        </Food>
        <Food>
            <Name>Papas Fritas</Name>
            <Price>20</Price>
        </Food>
    </Category>
    <Category name="Bebidas">
        <Food>
            <Name>Pepsi</Name>
            <Price>30</Price>
        </Food>
        <Food cocina="si">
            <Name>Coca Cola</Name>
            <Price>40</Price>
        </Food>
    </Category>
</Menu>

What I want to do is go through each <Category> checking if the attribute is what I need, for example "Bebidas", so the part I'm interested in is:

<Food>
    <Name>Pepsi</Name>
    <Price>30</Price>
</Food>
<Food cocina="si">
    <Name>Coca Cola</Name>
    <Price>40</Price>
</Food>

Now that I have this I want to do something similar to what I have done already:

First I want to print out all:

Pepsi 30
Coca Cola 40

And the I want to print out only the ones that food had the attribute cocina="si", so:

Coca Cola 40

So I have various questions:

First of all which approach to use, I am confused by the abundance of possible methods and implementations: XmlDocument, XmlReader, XmlTextReader, etc.

From this question I gather XmlDocument is the easier to use, that would be great, the simpler, the better as I am quite new to parsing Xml files as you can appreciate.

Now to the actual implementation, I have tried all sort of things with not much succes, I seem to be able to do some parts but not all together.

XmlNodeList elemList = doc.GetElementsByTagName("Category");
for (int i = 0; i < elemList.Count; i++)
{
    Console.WriteLine(elemList[i].InnerXml);
}

This will output:

<Food><Name>Haburguesa</Name><Price>10</Price></Food><Food><Name>Papas Fritas</Name><Price>20</Price></Food>
<Food><Name>Pepsi</Name><Price>30</Price></Food><Food><Name>Coca Cola</Name><Price>40</Price></Food> 

Which makes sense but now, how do I check if the category has the attribute name="cocina"?

I'm guessing something like this could help:

for (int j = 0; j < elemList[i].Attributes.Count; j++)
{
    //??                
}

But I can't find something like MoveToAttribute() in XmlTextReader.

And then again, how do I check whether has the attribute cocina="si"?

3

There are 3 answers

3
MarcinJuraszek On BEST ANSWER

I think LINQ to XML would be the easiest method here:

You have to use XDocument class here. Create your document object using static methods XDocument.Parse(DOCUMENT) - to load document from string - or XDocument.Load(PATH) - to load a document from file with given path.

After that you can easily find what you are looking for by a query like that:

var doc = XDocument.Parse("<Menu> ... </Menu>");
var results = doc.Descendants("Category")
                 .Where(cat => (string)cat.Attribute("name") == "Bebidas")
                 .SelectMany(cat => cat.Elements("Food"))
                 .Where(food => (string)food.Attribute("cocina") == "si")
                 .Select(food => string.Format("{0} {1}", food.Element("Name"), food.Element("Price"))).ToList();

To make it more clear I'll try to describe what that query does:

  1. Takes all elements named "Category" that are descendants of document root element
  2. Selects only that categories, which "name" attribute has value "Bebidas"
  3. Projects "Food" elements within that categories into one collection
  4. Selects "food" elements with "cocina" attribute value equal to "si"
  5. Projects result elements into a string formatted "name - price"
0
Steve H. On

You should be using XPATH, instead of writing code to retrieve these nodes.

The XPath expression you want is "//Food[@cocina = 'si']"

IE uses doc.selectNodes(xpath) as the way to use XPath, good browsers use doc.evaluate().

Take a look here: http://www.w3schools.com/xpath/xpath_examples.asp

0
Aaron Proctor On

LINQ to XML is fine but can get messy fast. If you're going to be using XML, you should know how to use XPaths to filter the nodes you need.

Try this:

foreach (XmlNode node in doc.SelectNodes("//Food[@cocina = 'si']"))
{
    Console.WriteLine(node.SelectSingleNode("Name").InnerText
        + " " 
        + node.SelectSingleNode("Price").InnerText);
}

XPaths are supported everywhere; LINQ to XML is no good to you outside of .NET.