web-dev-qa-db-fra.com

Interroger un XDocument sur les éléments par leur nom à n'importe quelle profondeur

J'ai un objet XDocument. Je souhaite interroger des éléments portant un nom particulier à n'importe quelle profondeur à l'aide de LINQ. Lorsque j'utilise Descendants("element_name"), je ne reçois que des éléments qui sont des enfants directs du niveau actuel. Ce que je recherche, c'est l'équivalent de "// nom_élément" dans XPath ... devrais-je simplement utiliser XPath, ou existe-t-il un moyen de le faire à l'aide des méthodes LINQ? Merci.

139
Rich

Les descendants devraient fonctionner parfaitement. Voici un exemple:

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

Résultats:

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

206
Jon Skeet

Un exemple indiquant l'espace de nom:

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

Vous pouvez le faire de cette façon:

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

xml est un XDocument.

Sachez que la propriété Name renvoie un objet qui a LocalName et Namespace. C'est pourquoi vous devez utiliser Name.LocalName si vous voulez comparer par nom.

35

Les descendants feront exactement ce dont vous avez besoin, mais assurez-vous d'avoir inclus un nom d'espace de noms avec le nom de l'élément. Si vous l'omettez, vous obtiendrez probablement une liste vide.

22
Nenad Dobrilovic

Il y a deux façons d'accomplir cela,

  1. Linq-to-xml
  2. XPath

Voici des exemples d'utilisation de ces approches,

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

Si vous utilisez XPath, vous devez manipuler IEnumerable:

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

Notez que

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

résulte soit un pointeur nul, soit aucun résultat.

11
roland roos

J'utilise la méthode d'extension XPathSelectElements qui fonctionne de la même manière que la méthode 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);
            }
        }
    }
}
7
Tahir Hassan

Suite à la réponse de @Francisco Goldenstein, j’ai écrit une méthode d’extension

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

nous savons que ce qui précède est vrai. Jon n'a jamais tort. les souhaits de la vie réelle peuvent aller un peu plus loin

<ota:OTA_AirAvailRQ
    xmlns:ota="http://www.opentravel.org/OTA/2003/05" EchoToken="740" Target=" Test" TimeStamp="2012-07-19T14:42:55.198Z" Version="1.1">
    <ota:OriginDestinationInformation>
        <ota:DepartureDateTime>2012-07-20T00:00:00Z</ota:DepartureDateTime>
    </ota:OriginDestinationInformation>
</ota:OTA_AirAvailRQ>

Par exemple, le problème est généralement de savoir comment obtenir EchoToken dans le document XML ci-dessus. Ou comment rendre flou l'élément avec le nom attrbute.

1- Vous pouvez les trouver en accédant avec l'espace de noms et le nom comme ci-dessous

doc.Descendants().Where(p => p.Name.LocalName == "OTA_AirAvailRQ").Attributes("EchoToken").FirstOrDefault().Value

2- Vous pouvez le trouver par la valeur du contenu de l'attribut, comme celui-ci

0
Hamit YILDIRIM

Ceci ma variante de la solution basée sur la méthode Linq et Descendants de la classe XDocument

using System;
using System.Linq;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        XDocument xml = XDocument.Parse(@"
        <root>
          <child id='1'/>
          <child id='2'>
            <subChild id='3'>
                <extChild id='5' />
                <extChild id='6' />
            </subChild>
            <subChild id='4'>
                <extChild id='7' />
            </subChild>
          </child>
        </root>");

        xml.Descendants().Where(p => p.Name.LocalName == "extChild")
                         .ToList()
                         .ForEach(e => Console.WriteLine(e));

        Console.ReadLine();
    }
}

Résultats:

Pour plus de détails sur la méthode Desendants, regardez ici.

0
Mselmi Ali