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.
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" />
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 );
}
, Vous pouvez le faire de cette façon:
xml.Descendants().Where(p => p.Name.LocalName == "Name of the node to find")
Où 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.
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.
Il y a deux façons d'y parvenir,
- Linq-to-xml
- 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.
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);
}
}
}
}
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);
}
}
}
(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
- jdoe@set.ca
- jsmith@hit.ca
- 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.