Consulta un XDocument para elementos por nombre a cualquier profundidad

Tengo un objeto XDocument . Deseo consultar elementos con un nombre particular a cualquier profundidad usando LINQ. Cuando uso Descendants("element_name") , solo obtengo elementos que son hijos directos del nivel actual. Lo que estoy buscando es el equivalente de “// element_name” en XPath … ¿debería usar XPath , o hay una forma de hacerlo con los métodos LINQ? Gracias.

Los descendientes deberían trabajar absolutamente bien. Aquí hay un ejemplo:

 using System; using System.Xml.Linq; class Test { static void Main() { string xml = @"       "; XDocument doc = XDocument.Parse(xml); foreach (XElement element in doc.Descendants("grandchild")) { Console.WriteLine(element); } } } 

Resultados:


Un ejemplo que indica el espacio de nombres:

 String TheDocumentContent = @"            "; 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 ); } 

Puedes hacerlo de esta manera:

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

donde xml es un XDocument .

Tenga en cuenta que el Name propiedad devuelve un objeto que tiene un LocalName y un Namespace . Es por eso que debe usar Name.LocalName si desea comparar por nombre.

Los descendientes harán exactamente lo que necesiten, pero asegúrese de haber incluido un nombre de espacio de nombres junto con el nombre del elemento. Si lo omite, probablemente obtendrá una lista vacía.

Hay dos maneras de lograr esto,

  1. Linq-to-xml
  2. XPath

Las siguientes son muestras del uso de estos enfoques,

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

Si utilizas XPath, necesitas hacer algo de manipulación con IEnumerable:

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

Tenga en cuenta que

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

da como resultado un puntero nulo o ningún resultado.

Estoy usando el método de extensión XPathSelectElements que funciona de la misma manera que el 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( @"  john   fred   mark  "); foreach (var childElem in xdoc.XPathSelectElements("//child")) { string childName = childElem.Element("name").Value; Console.WriteLine(childName); } } } } 

Siguiendo la respuesta de @Francisco Goldenstein, escribí un método de extensión

 using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace Mediatel.Framework { public static class XDocumentHelper { public static IEnumerable DescendantElements(this XDocument xDocument, string nodeName) { return xDocument.Descendants().Where(p => p.Name.LocalName == nodeName); } } } 

(El código y las instrucciones son para C # y es posible que deba modificarse levemente para otros idiomas)

Este ejemplo funciona perfecto si desea leer desde un nodo principal que tenga muchos elementos secundarios, por ejemplo, consulte el siguiente XML;

   jdoe@set.ca jsmith@hit.ca rgreen@set_ig.ca  

Ahora con este código a continuación (tenga en cuenta que el archivo XML se almacena en los recursos (consulte los enlaces al final del fragmento para obtener ayuda sobre los recursos). Puede obtener cada dirección de correo electrónico dentro de la etiqueta “correos electrónicos”.

 XDocument doc = XDocument.Parse(Properties.Resources.EmailAddresses); var emailAddresses = (from emails in doc.Descendants("emailAddress") select emails.Value); foreach (var email in emailAddresses) { //Comment out if using WPF or Windows Form project Console.WriteLine(email.ToString()); //Remove comment if using WPF or Windows Form project //MessageBox.Show(email.ToString()); } 

Resultados

  1. jdoe@set.ca
  2. jsmith@hit.ca
  3. rgreen@set_ig.ca

Nota: Para la aplicación de consola y WPF o formularios de Windows, debe agregar “using System.Xml.Linq;” Con la directiva en la parte superior de su proyecto, para Console también necesitará agregar una referencia a este espacio de nombres antes de agregar la directiva Using. Además, para la consola no habrá ningún archivo de recursos de forma predeterminada en la “carpeta de propiedades”, por lo que deberá agregar manualmente el archivo de recursos. Los artículos de MSDN a continuación, explican esto en detalle.

Agregar y editar recursos

Cómo agregar o eliminar recursos