2010-10-13 20 views
15

Je voudrais construire une requête XPath qui retournera un élément "div" ou "table", pourvu qu'il y ait un descendant contenant le texte "abc". Le seul inconvénient est qu'il ne peut pas avoir de descendants div ou table.Requête XPath avec les prédicats descendant et descendant()

<div> 
    <table> 
    <form> 
     <div> 
     <span> 
      <p>abcdefg</p> 
     </span> 
     </div> 
     <table> 
     <span> 
      <p>123456</p> 
     </span> 
     </table> 
    </form> 
    </table> 
</div> 

Ainsi, le seul résultat correct de cette requête serait:

/div/table/form/div 

Ma meilleure tentative ressemble à ceci:

//div[contains(//text(), "abc") and not(descendant::div or descendant::table)] | //table[contains(//text(), "abc") and not(descendant::div or descendant::table)] 

mais ne retourne pas le résultat correct.

Merci pour votre aide.

+0

Bonne question, +1. Voir ma réponse pour ce qui est probablement la solution la plus courte. :) –

Répondre

32

Quelque chose de différent: :)

//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] 

semble beaucoup plus court que les autres solutions, non? :)

Traduit en anglais simple: Pour tout nœud de texte dans le document qui contient la chaîne "abc" sélectionner son premier ancêtre qui est soit un div ou un table.

Ceci est plus efficace, car seule une analyse complète de l'arborescence des documents (et non tout autre) est nécessaire, et la traversal ancestor::* est vraiment pas cher par rapport à une analyse descendent:: (arbre).

Pour vérifier que cette solution "fonctionne vraiment":

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] "/> 
</xsl:template> 
</xsl:stylesheet> 

lorsque cette transformation est effectuée sur le document XML fourni:

<div> 
    <table> 
    <form> 
     <div> 
     <span> 
      <p>abcdefg</p> 
     </span> 
     </div> 
     <table> 
     <span> 
      <p>123456</p> 
     </span> 
     </table> 
    </form> 
    </table> 
</div> 

le résultat recherché, correct est produit:

<div> 
    <span> 
     <p>abcdefg</p> 
    </span> 
</div> 

Remarque: Il n'est pas nécessaire d'utiliser XSLT - tout hôte XPath 1.0 tel que DOM doit obtenir le même résultat.

+1

merci pour votre réponse et merci pour le +1. Je préfère la compacité de cette réponse, mais je n'arrive pas à la faire fonctionner dans mes tests. Les deux autres réponses à cette question fonctionnent pour moi. Est-il possible qu'il y ait une faute de frappe dans votre réponse? Je ne peux pas prétendre comprendre tout cela.Que fait le [1]? Encore une fois, si vous avez un aperçu de la raison pour laquelle cette réponse ne fonctionne pas pour moi et les autres, je l'apprécierais. Je voudrais +1 pour votre temps mais je suis nouveau sur ce site et n'ai pas encore la capacité. Merci. – juan234

+0

@ juan234: J'ai ajouté à ma réponse un code de vérification que tout le monde peut exécuter et vérifier l'exactitude du résultat. Cette vérification montre l'exactitude de l'expression - il n'y a pas de faute de frappe. Vous pouvez avoir des problèmes pour différentes raisons: d'utiliser le moteur XPath 1.0 incompliant aux problèmes dans votre code - pour identifier la raison pour laquelle il est nécessaire de voir votre code. '[1]' signifie le premier noeud du jeu de nœuds sélectionné par la partie de l'expression qui est immédiatement à droite de '[1]' - dans les axes inverses (comme 'ancestor ::' cela signifie en fait le dernier nœud dans l'ordre du document). –

+0

Je suis convaincu :) – juan234

1

vous pouvez essayer:

//div[ 
    descendant::text()[contains(., "abc")] 
    and not(descendant::div or descendant::table) 
] | 
//table[ 
    descendant::text()[contains(., "abc")] 
    and not(descendant::div or descendant::table) 
] 

fait ça aide?

1
//*[self::div|self::table] 
    [descendant::text()[contains(.,"abc")]] 
    [not(descendant::div|descendant::table)] 

Le problème est que les fonctions contains(//text(), "abc") ensembles de noeuds coulés en prenant le premier noeud.