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

J'ai un XDocument objet. Je veux interroger des éléments avec un nom particulier à n'importe quelle profondeur en utilisant LINQ. Lorsque j'utilise Descendants("element_name"), Je n'obtiens que des éléments qui sont des enfants directs du niveau actuel. Ce que je cherche est l'équivalent de "/ / element_name " dans XPath...devrais - je simplement utiliser XPath, ou Existe-t-il un moyen de le faire en utilisant les méthodes LINQ? Grâce.

134
demandé sur Dave Zych 2009-02-19 19:46:09

8 réponses

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

193
répondu Jon Skeet 2009-02-19 16:48:20

Un exemple indiquant l'espace de noms:

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 );
}
50
répondu Jelgab 2009-06-23 18:46:17

, 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 retourne un objet qui a un LocalName et a Namespace. C'est pourquoi vous devez utiliser Name.LocalName si vous voulez comparer par son nom.

27
répondu Francisco Goldenstein 2014-04-08 07:05:27

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
répondu Nenad Dobrilovic 2009-02-19 16:52:40

Il y a deux façons d'y parvenir,

  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 faire quelques manipulations avec 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
répondu roland roos 2012-05-03 13:09:58

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);
            }
        }
    }
}
6
répondu Tahir Hassan 2013-02-08 14:52:00

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
répondu Tiago Freitas Leal 2017-02-10 12:11:10

(le Code et les Instructions sont pour C# et peuvent avoir besoin d'être légèrement modifiés pour d'autres langues)

Cet exemple fonctionne parfaitement si vous voulez lire à partir d'un nœud Parent qui a beaucoup d'enfants, par exemple, regardez le XML suivant;

<?xml version="1.0" encoding="UTF-8"?> 
<emails>
    <emailAddress>jdoe@set.ca</emailAddress>
    <emailAddress>jsmith@hit.ca</emailAddress>
    <emailAddress>rgreen@set_ig.ca</emailAddress> 
</emails>

Maintenant, avec ce code ci-dessous (en gardant à l'esprit que le fichier XML est stocké dans les ressources (voir les liens à la fin de l'extrait pour obtenir de l'aide sur les ressources), vous pouvez obtenir chaque adresse e-mail dans les " emails" balise.

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

Résultats

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

Remarque: Pour L'Application Console et les formulaires WPF ou Windows, vous devez ajouter le " utilisation du système.XML.Linq; " en utilisant la directive en haut de votre projet, pour la Console, vous devrez également ajouter une référence à cet espace de noms avant d'ajouter la directive Using. Aussi pour la Console, il n'y aura pas de fichier de ressources par défaut sous le "dossier des propriétés", donc vous devez ajoutez manuellement le fichier de ressources. Les articles MSDN ci-dessous expliquent cela en détail.

Ajout et Modification des Ressources

Comment: Ajouter ou Supprimer des Ressources

-1
répondu Ravi Ramnarine 2011-11-18 11:36:57