【问题标题】:How do I get all the subnodes of a node with certain attribute?如何获取具有特定属性的节点的所有子节点?
【发布时间】:2012-06-05 02:48:07
【问题描述】:

我有一个这样的 xml 文档:

<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>

我要做的是遍历每个&lt;Category&gt; 检查属性是否是我需要的,例如“Bebidas”,所以我感兴趣的部分是:

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

现在我有了这个,我想做一些类似于我已经做过的事情:

首先我要全部打印出来:

Pepsi 30
Coca Cola 40

我只想打印出 food 具有 cocina="si" 属性的那些,所以:

Coca Cola 40

所以我有各种各样的问题:

首先要使用哪种方法,我对大量可能的方法和实现感到困惑:XmlDocument、XmlReader、XmlTextReader 等。

来自this question,我认为 XmlDocument 更易于使用,那就太好了,越简单越好,因为我对解析 Xml 文件还很陌生,你可以理解。

现在到了实际的实现,我尝试了各种各样的事情,但没有多少成功,我似乎可以做一些部分,但不能一起做。

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

这将输出:

<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> 

这很有意义,但现在,我如何检查该类别是否具有属性 name="cocina"

我猜这样的事情会有所帮助:

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

但我在 XmlTextReader 中找不到类似 MoveToAttribute() 的内容。

还有,我如何检查是否有属性cocina="si"

【问题讨论】:

    标签: c# xml parsing xmldocument xmltextreader


    【解决方案1】:

    您应该使用 XPATH,而不是编写代码来检索这些节点。

    您想要的 XPath 表达式是“//Food[@cocina = 'si']”

    IE 使用 doc.selectNodes(xpath) 作为 XPath 的使用方式,好的浏览器使用 doc.evaluate()。

    看这里:http://www.w3schools.com/xpath/xpath_examples.asp

    【讨论】:

      【解决方案2】:

      我认为 LINQ to XML 将是这里最简单的方法:

      你必须在这里使用XDocument 类。使用静态方法 XDocument.Parse(DOCUMENT) 创建您的文档对象 - 从字符串加载文档 - 或 XDocument.Load(PATH) - 从给定路径的文件加载文档。

      之后,您可以通过这样的查询轻松找到您要查找的内容:

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

      为了更清楚,我将尝试描述该查询的作用:

      1. 获取所有名为“Category”且属于文档根元素的后代的元素
      2. 仅选择“name”属性值为“Bebidas”的类别
      3. 将该类别中的“食物”元素投射到一个集合中
      4. 选择“cocina”属性值等于“si”的“food”元素
      5. 将结果元素投影到格式为“名称 - 价格”的字符串中

      【讨论】:

      • 我有一个问题,我不确定如何使用results。每个人不应该简单吗?另外,如果我从文件中读取,我应该使用 XDocument.Load() 对吗?
      • 另外我似乎找不到错误在哪里,因为Console.WriteLine(results.Count); 输出 0。
      • 现在似乎工作得很好,谢谢,对不起所有的 cmets :)
      【解决方案3】:

      LINQ to XML 很好,但很快就会变得混乱。如果你打算使用 XML,你应该知道如何使用 XPaths 来过滤你需要的节点。

      试试这个:

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

      XPath 无处不在; LINQ to XML 在 .NET 之外对您没有好处。

      【讨论】: