在XDocument中查找元素?

61

我有一个简单的XML

<AllBands>
  <Band>
    <Beatles ID="1234" started="1962">greatest Band<![CDATA[lalala]]></Beatles>
    <Last>1</Last>
    <Salary>2</Salary>
  </Band>
  <Band>
    <Doors ID="222" started="1968">regular Band<![CDATA[lalala]]></Doors>
    <Last>1</Last>
    <Salary>2</Salary>
  </Band>
</AllBands>

然而,

当我想要访问“Doors乐队”并更改它的ID时:

  using (var stream = new StringReader(result))
            {
                XDocument xmlFile = XDocument.Load(stream);

                var query = from c in xmlFile.Elements("Band")

                            select c;
                             ...

查询 没有结果

但是,如果我写 xmlFile.Elements().Elements("Band"),那么它会找到。

问题在哪里?

需要从根目录指定完整路径吗?

如果需要,为什么没有指定AllBands也能工作?

XDocument导航是否要求我知道所需元素的完整级别结构?

7个回答

94

Elements() 只会检查直接子元素 - 在第一个情况中是根元素,在第二个情况中是根元素的子元素,因此你会在第二种情况中得到匹配结果。如果你想要匹配任何后代元素,请使用 Descendants() 替代:

var query = from c in xmlFile.Descendants("Band") select c;

我建议您重新构建Xml结构:乐队名称应该作为属性或元素值,而不是元素名称本身-这会使查询(以及模式验证)更加困难,例如:

<Band>
  <BandProperties Name ="Doors" ID="222" started="1968" />
  <Description>regular Band<![CDATA[lalala]]></Description>
  <Last>1</Last>
  <Salary>2</Salary>
</Band>

1
是的,我知道应该是Name元素,里面应该是Beatles/Doors...谢谢解释。这只是一个快速的XML测试,我还没有深入考虑过一个例子。 - Royi Namir
谢谢!如果您能添加一些命名空间的示例,那就太好了。例如:XNamespace xsiNs = "http://www.w3.org/2001/XMLSchema-instance"; var element = productionPackage.Descendants(xsiNs + "ElementToFind") - Daniel Eisenreich

41
你可以用这种方式做:
xml.Descendants().SingleOrDefault(p => p.Name.LocalName == "Name of the node to find")

其中 xml 是一个 XDocument。

请注意,Name 属性返回一个包含 LocalName 和 Namespace 的对象。因此,如果您想按名称进行比较,必须使用 Name.LocalName。


31
你应该使用Root来引用根元素:
xmlFile.Root.Elements("Band")

如果你想在整个文档中查找元素,使用Descendants

xmlFile.Descendants("Band")

9

问题在于Elements只能获取调用它的元素的直接子元素。如果要获取所有后代元素,请使用Descendants方法:

var query = from c in xmlFile.Descendants("Band")

7

在处理大而复杂的XML文件时,我的经验是有时候无论是Elements还是Descendants都不能检索到特定的元素(我仍然不知道为什么)。

在这种情况下,我发现手动搜索元素是一个更安全的选择,正如以下MSDN帖子所述:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/3d457c3b-292c-49e1-9fd4-9b6a950f9010/how-to-get-tag-name-of-xml-by-using-xdocument?forum=csharpgeneral

简而言之,您可以创建一个 GetElement 函数:
private XElement GetElement(XDocument doc,string elementName)
{
    foreach (XNode node in doc.DescendantNodes())
    {
        if (node is XElement)
        {
            XElement element = (XElement)node;
            if (element.Name.LocalName.Equals(elementName))
                return element;
        }
    }
    return null;
}

然后您可以像这样调用它:
XElement element = GetElement(doc,"Band");

请注意,如果找不到匹配的元素,则会返回null

3
< p > Elements() 方法返回一个包含当前节点所有子元素的 IEnumerable<XElement> 类型。 对于 XDocument,该集合仅包含 Root 元素。 因此需要执行以下操作:

var query = from c in xmlFile.Root.Elements("Band")
            select c;

1

Sebastian的答案是我在检查xaml文档时唯一有效的答案。如果您想要所有元素的列表,那么该方法看起来很像上面Sebastian的答案,只需返回一个列表...

    private static List<XElement> GetElements(XDocument doc, string elementName)
    {
        List<XElement> elements = new List<XElement>();

        foreach (XNode node in doc.DescendantNodes())
        {
            if (node is XElement)
            {
                XElement element = (XElement)node;
                if (element.Name.LocalName.Equals(elementName))
                    elements.Add(element);
            }
        }
        return elements;
    }

这样称呼它:

var elements = GetElements(xamlFile, "Band");

或者在我的xaml文档中,如果我想要所有的TextBlock,就这样调用它:
var elements = GetElements(xamlFile, "TextBlock");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接