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
.
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>
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']]
À 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.
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.