1. Généralités

Un prédicat commence par [et se termine par].

Un prédicat peut contenir des XPath et ainsi d'autres prédicats.

Un prédicat est une condition, on peut le comparer à la clause WHERE en SQL, il en diffère néanmoins par le fait qu'il ne porte pas sur l'intégralité du Xpath (sauf usage particulier de parenthèses) mais sur la combinaison axe+test qu'il suit directement : son contexte.

2. Opérateurs

2-A. Les opérateurs booléens

Les booléens sont true() et false(). Le noeud vide, la chaîne vide et zéro sont convertis en false().

  • NON : c'est une fonction en XPath, not(...), elle englobe la partie sur laquelle porte la négation ;
  • OU : or ;
  • ET : and ;
  • EGAL et DIFFERENT : = et != attention != n'est pas la négation de =, comme cela sera détaillé plus tard.
  • COMPARATEURS D'ORDRE : <=, <, >=, >.

2-B. Les opérateurs numériques

Si une de ces opérations est effectuée sur une chaîne de caractères la valeur renvoyée est NaN(Not a Number).

  • ADDITION : + ;
  • SOUSTRACTION : -, attention sur l'opérateur de soustraction, il faut toujours le faire précéder et suivre d'un espace sinon l'expression peut être confondue avec un nom d'élément ;
  • MULTIPLICATION : * ;
  • DIVISION: div ;
  • MODULO: mod.

3. Test

Lors de test entraînant une comparaison tout noeud est converti en sa valeur textuelle. Celle-ci pourra être considérée comme un nombre (si sa forme le permet) ou une chaîne de caractères.

3-A. Test de valeur

Il porte sur la valeur textuelle du noeud sélectionné. On ne peut comparer que des types simples.
On notera ici l'importance du self::node() et de son raccourci «.».

Xpath : //AA[.=1]

<ROOT>

    <AA>1</AA>

    <AA>2</AA>

</ROOT>

Xpath : //AA[. > 1]

<ROOT>

    <AA>1</AA>

    <AA>2</AA>

</ROOT>

3-B. Test d'existence

On souhaite sélectionner un noeud en fonction de ses fils/père/attributs/etc. Dans ce cas, le XPath présent dans le prédicat sera évalué à partir du noeud sélectionné précédant ce même prédicat, il ne sera donc pas précédé de /.

Xpath : //AA[DD] (tous les AA possédant au moins un fils DD)

<ROOT>

    <AA>

        <BB>

            <CC/>

        </BB>

        <DD/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

Xpath : //BB[*] (tous les BB possédant au moins un fils element)

<ROOT>

    <AA>

        <BB>

            <CC/>

        </BB>

        <DD/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

Pour bien comprendre l'importance des chemins relatifs, le XPath suivant //AA[/*//AA/DD] se traduit par : tous les AA à condition qu'il existe un AA descendant de la racine possédant au moins un fils DD.

3-C. Test de position

On utilisera pour ceci une fonction XPath.

La fonction position() renvoie la position du noeud en lecture dans son contexte parent. La numérotation des positions en XPath commence à 1.

Xpath : /ROOT/AA[position()=2]

ou /ROOT/AA[2](écriture raccourcie) sélectionne le deuxième fils AA de ROOT

<ROOT>

    <AA>

        <BB>

            <CC/>

        </BB>

        <DD/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

Quand on teste les positions, sauf si on utilise les parenthèses comme indiqué dans le chapitre 4, le contexte parent est le noeud parent.
Ainsi un XPath du type //*[2] ne renvoie pas le deuxième noeud de la sélection mais tous les noeuds sélectionnés précédemment qui sont des deuxièmes fils.

Xpath : //*[2]

<ROOT>

    <AA>

        <BB>

            <CC/>

        </BB>

        <DD/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

3-D. Différence entre = et !=

La différence entre = , != et leur négation se fait sensible lors de comparaison sur des ensembles.

Voyons déjà le comportement sur les ensembles.

Si on compare une valeur à un ensemble de noeuds via = , l'expression renvoie vrai si la valeur textuelle d'un des noeuds est égale à la valeur du test.

Xpath: //a[.=//b]

<r>

    <a>1</a>

    <a>5</a>

    <b>1</b>

    <b>2</b>

    <b>3</b>

</r>

Si on compare une valeur à un ensemble de noeuds via != , l'expression renvoie vrai si la valeur textuelle d'un des noeuds est différente de la valeur du test.

Xpath: //a[.!=//b]

<r>

    <a>1</a>

    <a>5</a>

    <b>1</b>

    <b>2</b>

    <b>3</b>

</r>

Si on inclut l'expression avec = dans une négation, l'expression renvoie vrai si toutes les valeurs textuelles sont différentes de la valeur du test.

Xpath: //a[not(.=//b)]

<r>

    <a>1</a>

    <a>5</a>

    <b>1</b>

    <b>2</b>

    <b>3</b>

</r>

Si on inclut l'expression avec != dans une négation , l'expression renvoie vrai si toutes les valeurs textuelles sont égales à la valeur du test.

Xpath: //a[not(.!=//b)]

<r>

    <a>1</a>

    <a>5</a>

    <b>1</b>

    <b>2</b>

    <b>3</b>

</r>

Xpath: //a[not(.!=//b)]

<r>

    <a>1</a>

    <a>5</a>

    <b>1</b>

    <b>1</b>

</r>

4. Parenthèses et contexte

Les parenthèses sont utilisables non seulement à l'intérieur des prédicats mais aussi sur les chemins Xpath.

A l'intérieur d'un prédicat les parenthèses suivent les règles de priorité classiques des opérations logiques.

La syntaxe XPath nécessite que la parenthèse gauche soit toujours placée en tête du chemin, ainsi :

(/AA/DD) est bon, /AA(/DD) ou /AA/(DD) est faux.

La parenthèse va permettre de considérer tout le Xpath englobé comme un ensemble de noeuds sans contexte.

Ainsi pour les prédicats, en particulier sur ceux effectuant des tests de position, on ne tiendra plus compte du contexte du noeud.

Quelques exemples avec et sans parenthèses pour comparaison :

Xpath : /ROOT/AA/BB[position()=2]

<ROOT>

    <AA>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <BB/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

Xpath :( /ROOT/AA/BB)[position()=2]

<ROOT>

    <AA>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <BB/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

Xpath : /ROOT/AA/BB[position()=1]

<ROOT>

    <AA>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <BB/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

Xpath :( /ROOT/AA/BB)[position()=1]

<ROOT>

    <AA>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <BB/>

    </AA>

    <AA>

        <BB/>

    </AA>

</ROOT>

5. Quelques exemples combinés

Après avoir vu en détail les différents types d'opérateurs et de tests, la démonstration sera complète avec quelques exemples de combinaisons de ces notions.

5-A. Un noeud avec au moins N fils

Si on souhaite un noeud avec N fils, cela signifie qu'il possède au moins un fils de position N.
On le traduira par une syntaxe du type : Chemin_XPath[*[position()=N]].

Xpath : /ROOT/AA[*[position()=2]]

<ROOT>

    <AA>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <BB/>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <BB/>

    </AA>

</ROOT>

5-B. Enième élément par ordre d'apparition

Il n'est pas toujours évident de repérer le énième élément d'une structure arborescente. Pour ceci les parenthèses nous seront d'une grande aide.

Xpath : (//BB)[3]

<ROOT>

    <BB>

        <BB/>

    </BB>

    <AA>

        <BB/>

        <BB/>

    </AA>

</ROOT>

5-C. Fils B d'un élément A si celui-ci possède aussi un fils C

Nous sommes simplement dans le cas où, au lieu d'être à la fin du XPath, le prédicat est au milieu.

Xpath : /ROOT/AA[CC]/BB

<ROOT>

    <AA>

        <BB/>

    </AA>

    <AA>

        <BB/>

        <CC/>

    </AA>

</ROOT>

5-D. Fils C d'un élément B ou A

Il existe différentes façons de coder ceci, la méthode présentée ici a l'avantage de rester simple et d'être très peu coûteuse.

Xpath : /ROOT/*[self::AA or self::BB]/CC

<ROOT>

    <BB>

        <CC/>

    </BB>

    <EE>

        <CC/>

    </EE>

    <AA>

        <BB/>

        <CC/>

    </AA>

</ROOT>

5-E. Suppression de doublons

La technique est simple bien que gourmande en ressources. Pour éliminer les doublons, il suffit d'éliminer les éléments qui ont la particularité d'être précédés par un élément qui leur est identique.
Nous utiliserons pour ceci les axes preceding ou preceding-sibling.

Xpath: /r/*[not(preceding-sibling::*=.)]

<r>

    <a>1</a>

    <a>5</a>

    <b>1</b>

    <b>2</b>

    <b>5</b>

</r>