it-swarm.dev

Consultar um XDocument para elementos pelo nome em qualquer profundidade

Eu tenho um objeto XDocument. Eu quero consultar elementos com um nome específico em qualquer profundidade usando LINQ. Quando eu uso Descendants("element_name"), eu só recebo elementos que são filhos diretos do nível atual. O que eu estou procurando é o equivalente de "// element_name" no XPath ... devo apenas usar XPath, ou existe uma maneira de fazê-lo usando métodos LINQ? Obrigado.

137
Rich

Descendentes devem funcionar absolutamente bem. Aqui está um exemplo:

using System;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        string xml = @"
<root>
  <child id='1'/>
  <child id='2'>
    <grandchild id='3' />
    <grandchild id='4' />
  </child>
</root>";
        XDocument doc = XDocument.Parse(xml);

        foreach (XElement element in doc.Descendants("grandchild"))
        {
            Console.WriteLine(element);
        }
    }
}

Resultados:

<grandchild id="3" />
<grandchild id="4" />

202
Jon Skeet

Um exemplo indicando o namespace:

String TheDocumentContent =
@"
<TheNamespace:root xmlns:TheNamespace = 'http://www.w3.org/2001/XMLSchema' >
   <TheNamespace:GrandParent>
      <TheNamespace:Parent>
         <TheNamespace:Child theName = 'Fred'  />
         <TheNamespace:Child theName = 'Gabi'  />
         <TheNamespace:Child theName = 'George'/>
         <TheNamespace:Child theName = 'Grace' />
         <TheNamespace:Child theName = 'Sam'   />
      </TheNamespace:Parent>
   </TheNamespace:GrandParent>
</TheNamespace:root>
";

XDocument TheDocument = XDocument.Parse( TheDocumentContent );

//Example 1:
var TheElements1 =
from
    AnyElement
in
    TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
select
    AnyElement;

ResultsTxt.AppendText( TheElements1.Count().ToString() );

//Example 2:
var TheElements2 =
from
    AnyElement
in
    TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
where
    AnyElement.Attribute( "theName" ).Value.StartsWith( "G" )
select
    AnyElement;

foreach ( XElement CurrentElement in TheElements2 )
{
    ResultsTxt.AppendText( "\r\n" + CurrentElement.Attribute( "theName" ).Value );
}
53
Jelgab

Você pode fazer assim:

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

onde xml é um XDocument.

Esteja ciente de que a propriedade Name retorna um objeto que possui um LocalName e um Namespace. É por isso que você precisa usar Name.LocalName se quiser comparar por nome.

32
Francisco Goldenstein

Descendentes farão exatamente o que você precisa, mas tenha certeza que você incluiu um nome de namespace junto com o nome do elemento. Se você omiti-lo, provavelmente obterá uma lista vazia.

22
Nenad Dobrilovic

Existem duas maneiras de conseguir isso

  1. Linq-para-xml
  2. XPath

A seguir, exemplos de uso dessas abordagens,

List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();

Se você usa o XPath, você precisa fazer alguma manipulação com o IEnumerable:

IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();

Observe que

var res = doc.XPathEvaluate("/emails/emailAddress");

resulta em um ponteiro nulo ou nenhum resultado.

11
roland roos

Eu estou usando o método de extensão XPathSelectElements que funciona da mesma maneira para o método XmlDocument.SelectNodes:

using System;
using System.Xml.Linq;
using System.Xml.XPath; // for XPathSelectElements

namespace testconsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            XDocument xdoc = XDocument.Parse(
                @"<root>
                    <child>
                        <name>john</name>
                    </child>
                    <child>
                        <name>fred</name>
                    </child>
                    <child>
                        <name>mark</name>
                    </child>
                 </root>");

            foreach (var childElem in xdoc.XPathSelectElements("//child"))
            {
                string childName = childElem.Element("name").Value;
                Console.WriteLine(childName);
            }
        }
    }
}
6
Tahir Hassan

Seguindo a resposta de @Francisco Goldenstein, escrevi um método de extensão

using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace Mediatel.Framework
{
    public static class XDocumentHelper
    {
        public static IEnumerable<XElement> DescendantElements(this XDocument xDocument, string nodeName)
        {
            return xDocument.Descendants().Where(p => p.Name.LocalName == nodeName);
        }
    }
}
1
Tiago Freitas Leal