c# linq - Consulta un XDocument para elementos por nombre a cualquier profundidad





example xelement (8)


Un ejemplo que indica el espacio de nombres:

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

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.




Hay dos maneras de lograr esto,

  1. Linq-to-xml
  2. XPath

Las siguientes son muestras del uso de estos enfoques,

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

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

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

Tenga en cuenta que

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

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




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<XElement> DescendantElements(this XDocument xDocument, string nodeName)
        {
            return xDocument.Descendants().Where(p => p.Name.LocalName == nodeName);
        }
    }
}



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

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




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.




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

<?xml version="1.0" encoding="UTF-8"?> 
<emails>
    <emailAddress>[email protected]</emailAddress>
    <emailAddress>[email protected]</emailAddress>
    <emailAddress>[email protected]_ig.ca</emailAddress> 
</emails>

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. [email protected]
  2. [email protected]
  3. [email protected]_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




La operación .Elements devuelve una LISTA de XElements, pero lo que realmente desea es un elemento SINGLE. Agrega esto:

XElement Contacts = (from xml2 in XMLDoc.Elements("Contacts").Elements("Node")
                    where xml2.Element("ID").Value == variable
                    select xml2).FirstOrDefault();

De esta forma, le dice a LINQ que le proporcione el primero (o NULO, si no hay ninguno) de esa LISTA de elementos XE que está seleccionando.

Bagazo





c# .net xml linq linq-to-xml