web-dev-qa-db-fra.com

XPath pour trouver l'élément de l'ancêtre le plus proche contenant un élément ayant un attribut avec une certaine valeur

ancestor::foo[bar[@attr="val"]]

Je pensais que cela fonctionnerait, mais ce n'est pas. J'ai besoin de trouver l'élément foo le plus proche qui monte dans l'arborescence qui a un élément enfant bar, qui à son tour possède un attribut attr de val.

18
Core Xii

Cela devrait fonctionner:

//*[ancestor::foo[bar[@attr="val"]]]

ou bien

root/foo/bar[ancestor::foo[bar[@attr="val"]]]

correspond au deuxième élément <bar>

<root>
    <foo>
        <bar attr="xxx"></bar>
    </foo>
    <foo>
        <bar attr="val"></bar>
    </foo>
    <foo>
        <bar attr="zzz"></bar>
    </foo>
</root>
30
Gordon

Mise à jour pour refléter une solution envisagée par OP.

Voir la réponse de @ David

Pour un exemple xml ci-dessous,

<root>
<foo id="0">
    <bar attr="val"/>
    <foo id="1">
        <bar attr="xxx"/>
    </foo>
    <foo id="2">
        <bar attr="val">
            <one>1</one>
            <two>
                <three>3</three>
            </two>
        </bar>
    </foo>
</foo>

un xpath valide sur l'axe inverse avec <trois> comme nœud de contexte, une solution valide est

./ancestor::foo[bar[@attr='val']][position() = 1]

"./" est facultatif. position () = 1 n'est pas facultatif, sinon l'ancêtre foo satisfaisant les prédicats serait également renvoyé.

Ancienne réponse: ignorer

C'est une vieille question. mais si vous êtes intéressé, voici ce qui vous est demandé ce que la question initiale visait.

Axe inverse

//bar[@attr='val']/ancestor::*[position()=1]

Axe avant

//*[child::bar[@attr='val']]
11
santon

À mon avis, la meilleure réponse a été fournie par la personne qui a posé la question. Sa solution était supposée rendre tous les foos d'ancêtres. Il veut seulement le plus proche qui est celui avec position () = 1, donc son expression xpath doit être légèrement modifiée pour:

ancestor::foo[bar[@attr="val"] and position() = 1]

S'il écrit que le ancestor::foo[bar[@attr="val"]] n'a rien renvoyé, il a donc rencontré un autre problème dans son xml ou dans son hypothèse concernant l'élément actuel de son contexte d'évaluation XPath.

Les autres réponses (commençant par //) ne répondent pas vraiment à la question et même si par hasard elles répondaient au besoin de quelqu'un, elles ne sont donc pas efficaces: Elles choisissent TOUS les éléments du fichier xml, en appliquant ensuite un filtre . Alors que le xpath relatif proposé par moi-même ou par la personne qui posait cette question va juste chercher dans quelques éléments à partir du nœud actuel jusqu'au parent et à son parent, etc. - cela sera très efficace même avec gros fichiers XML.

1
David L.

Si vous avez un fichier comme celui-ci:

<root>                                                                                                             
<foo id="0">                                                                                                       
    <foo id="1">                                                                                                   
        <bar attr="xxx" />                                                                                         
    </foo>                                                                                                         
    <foo id="2">                                                                                                   
        <bar attr="val" />                                                                                         
    </foo>                                                                                                         
    <foo id="3">                                                                                                   
        <tar>                                                                                                      
            <bar attr="val" />                                                                                     
        </tar>                                                                                                     
    </foo>                                                                                                         
</foo>                                                                                                             
</root>

et vous voulez les nœuds foo avec les ids 2 et 3, c’est-à-dire le foos le plus proche de leurs descendants bar ayant attr=val, les travaux suivants:

//foo[descendant::bar[@attr="val"] and not(descendant::foo)]

Si vous omettez and not(descendant::foo), vous obtenez en plus foo avec l'id 0.

Les autres réponses n'ont pas fonctionné pour moi sur le cas général de mon exemple.

Vous pouvez le tester en Python avec:

from lxml import etree
tree = etree.parse('example.xml')
foos = tree.xpath('//foo[descendant::bar[@attr="val"] and not(descendant::foo)]')
for foo in foos:
    print(foo.attrib)

qui imprime:

{'id': '2'}
{'id': '3'}

Notez que le xpath utilisé n'est pas efficace. J'aimerais apprendre un plus efficace.

0
Ivan Ogai