在XDocument中查找元素?

时间:2011-12-10 22:34:51

标签: c# .net xml linq-to-xml

我有一个简单的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>

然而,

当我想要到达“门乐队”并更改其ID时:

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

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

                            select c;
                             ...

query 没有结果

但是

如果我写xmlFile.Elements().Elements("Band")所以它确实找到了它。

有什么问题?

是否需要Root的完整路径?

如果是这样,为什么没有指定AllBands

就可以

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

7 个答案:

答案 0 :(得分:75)

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 :(得分:27)

你可以这样做:

xml.Descendants().SingleOrDefault(p => p.Name.LocalName == "Name of the node to find")

其中xml是XDocument。

请注意,属性Name返回一个具有LocalName和Namespace的对象。这就是为什么如果要按名称进行比较,必须使用Name.LocalName。

答案 2 :(得分:25)

您应该使用Root来引用根元素:

xmlFile.Root.Elements("Band")

如果您想在文档中的任何位置找到元素,请改用Descendants

xmlFile.Descendants("Band")

答案 3 :(得分:6)

问题是Elements只接受你所称的直接子元素。如果您想要所有后代,请使用Descendants方法:

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

答案 4 :(得分:4)

我在使用大型和大型机器人时的经验复杂的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函数:

static void Main(string[] args)
        {

            Request.ServicesExternalClient se = new Request.ServicesExternalClient();
            se.ClientCredentials.UserName.UserName = "UserName";
            se.ClientCredentials.UserName.Password = "Password1";
            BindingElementCollection elements =  se.Endpoint.Binding.CreateBindingElements();
            elements.Add(SecurityBindingElement.CreateUserNameForSslBindingElement());
            System.Uri uri = new System.Uri("--url--");
            se.Endpoint.ListenUri = uri;        
            StringBuilder xml = new StringBuilder();
            xml.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
                 xml.Append(@"<NewInvoiceRequest >");
                 xml.Append(@"<tracingLevel>OFF</tracingLevel>");
                 '
                 '
                 '
                 '
                 '
                 '
                 '
                 xml.Append(@"</NewInvoiceRequest>");      
                Request.NewInvoiceRequest newInvRequest = new Request.NewInvoiceRequest();             
                Request.Invoice inv = new Request.Invoice();
                using (MemoryStream stream = new MemoryStream())
                {
                    XmlSerializer s = new XmlSerializer(typeof(Request.NewInvoiceRequest));
                    StreamWriter sw = new StreamWriter(stream);                
                    using (StringReader sr = new StringReader(xml.ToString()))
                    {                    
                            newInvRequest = (Request.NewInvoiceRequest)s.Deserialize(sr);
                    }         
                }

                Request.NewInvoiceResponse res = se.createNewInvoice(newInvRequest);
        }

然后你可以这样打电话:

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

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

答案 5 :(得分:2)

Elements()方法返回包含当前节点的所有子元素的IEnumerable<XElement>。对于XDocument,该集合仅包含Root元素。因此,需要以下内容:

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

答案 6 :(得分:0)

Sebastian的答案是检查xaml文档时唯一对我有用的答案。如果像我一样,您想要一个所有元素的列表,则该方法看起来就像上面塞巴斯蒂安的答案,只是返回一个列表...

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

或者在我想要所有TextBlocks的xaml文档的情况下,这样调用它:

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