web-dev-qa-db-fra.com

Trouver un élément dans XDocument?

J'ai un simple XML

<AllBands>
  <Band>
    <Beatles ID="1234" started="1962">greatest Band<![CDATA[lalala]]></Beatles>
    <Last>1</Last>
    <Salary>2</Salary>
  </Band>
  <Band>
    <Doors ID="222" started="1968">regular Band<![CDATA[lalala]]></Doors>
    <Last>1</Last>
    <Salary>2</Salary>
  </Band>
</AllBands>

Cependant , 

quand je veux atteindre le "groupe Doors" et changer son identifiant: 

  using (var stream = new StringReader(result))
            {
                XDocument xmlFile = XDocument.Load(stream);

                var query = from c in xmlFile.Elements("Band")

                            select c;
                             ...

query n'a aucun résultat

Mais

Si j'écris xmlFile.Elements().Elements("Band") alors il le trouve.

Quel est le problème ?

Le chemin complet de la racine est-il nécessaire?

Et si oui, pourquoi cela a-t-il fonctionné sans spécifier AllBands?

Est-ce que la navigation XDocument nécessite que je connaisse la structure de niveaux complète jusqu'à l'élément requis?

49
Royi Namir

Elements() vérifie uniquement les enfants directs - dans le premier cas, il s'agit de l'élément racine, dans le deuxième cas, les enfants de l'élément racine, vous obtenez donc une correspondance dans le deuxième cas. Si vous voulez juste un descendant correspondant, utilisez plutôt Descendants():

var query = from c in xmlFile.Descendants("Band") select c;

De plus, je vous suggérerais de restructurer votre code XML: le nom du groupe devrait être un attribut ou une valeur d’élément, pas le nom de l’élément lui-même, ce qui rend la requête (et la validation du schéma) beaucoup plus difficile, c’est-à-dire quelque chose comme ceci:

<Band>
  <BandProperties Name ="Doors" ID="222" started="1968" />
  <Description>regular Band<![CDATA[lalala]]></Description>
  <Last>1</Last>
  <Salary>2</Salary>
</Band>
72
BrokenGlass

Vous pouvez le faire de cette façon:

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

où xml est un XDocument.

Sachez que la propriété Name renvoie un objet comportant un nom local et un espace de noms. C'est pourquoi vous devez utiliser Name.LocalName si vous souhaitez comparer par nom.

27

Vous devez utiliser Root pour faire référence à l’élément racine:

xmlFile.Root.Elements("Band")

Si vous souhaitez rechercher des éléments n'importe où dans le document, utilisez Descendants à la place:

xmlFile.Descendants("Band")
23
Mark Byers

Le problème est que Elements ne prend que les éléments enfants direct de ce que vous appelez. Si vous voulez tous les descendants, utilisez la méthode Descendants :

var query = from c in xmlFile.Descendants("Band")
6
Jon Skeet

Mon expérience de travail avec des fichiers XML volumineux et complexes est que parfois ni Elements ni Descendants ne semblent fonctionner pour récupérer un élément spécifique (et je ne sais toujours pas pourquoi). 

Dans de tels cas, j'ai trouvé qu'une option beaucoup plus sûre consiste à rechercher manuellement l'élément, comme décrit dans le message MSDN suivant:

https://social.msdn.Microsoft.com/Forums/vstudio/fr-3d457c3b-292c-49e1-9fd4-9b6a950f9010/how-to-get-tag-name -de-xml-by-using- xdocument? forum = csharpgeneral

En bref, vous pouvez créer une fonction GetElement:

private XElement GetElement(XDocument doc,string elementName)
{
    foreach (XNode node in doc.DescendantNodes())
    {
        if (node is XElement)
        {
            XElement element = (XElement)node;
            if (element.Name.LocalName.Equals(elementName))
                return element;
        }
    }
    return null;
}

Que vous pouvez ensuite appeler comme ceci:

XElement element = GetElement(doc,"Band");

Notez que ceci retournera null si aucun élément correspondant n'est trouvé.

4
Human Wannabe

La méthode Elements() renvoie un IEnumerable<XElement> contenant tous les éléments enfants du nœud actuel. Pour un document XDocument, cette collection ne contient que l'élément racine. Par conséquent, les éléments suivants sont requis:

var query = from c in xmlFile.Root.Elements("Band")
            select c;
2
Phil Klein

La réponse de Sebastian était la seule réponse qui fonctionnait pour moi lors de l'examen d'un document xaml. Si, comme moi, vous souhaitez une liste de tous les éléments, la méthode ressemblerait beaucoup à la réponse de Sebastian ci-dessus, mais ne renverrait qu'une liste ...

    private static List<XElement> GetElements(XDocument doc, string elementName)
    {
        List<XElement> elements = new List<XElement>();

        foreach (XNode node in doc.DescendantNodes())
        {
            if (node is XElement)
            {
                XElement element = (XElement)node;
                if (element.Name.LocalName.Equals(elementName))
                    elements.Add(element);
            }
        }
        return elements;
    }

Appelez-le ainsi:

var elements = GetElements(xamlFile, "Band");

ou dans le cas de ma doc xaml où je voulais tous les TextBlocks, appelez-le ainsi:

var elements = GetElements(xamlFile, "TextBlock");
0
Ewan