XPath 1.0 : fonctionnement des prédicats

Erwan Amoureux

Cet article étudie le fonctionnement des prédicats qui permettent de poser des conditions dans le langage XPath.
Il fait suite à XPath : Types, axes et éléments.

Un remerciement spécial pour avoir épluché mon orthographe et ma ponctuation à ClaudeLELOUP et _Max_.

Commentez

- Titre : XPath 1.0 : fonctionnement des prédicats

- Auteur : Erwan Amoureux

- Parution : 6 décembre 2010

- Mise à jour : 12 juillet 2020

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2010 Erwan Amoureux. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Généralités

- II. Opérateurs

- II-A. Les opérateurs booléens

- II-B. Les opérateurs numériques

- III. Test

- III-A. Test de valeur

- III-B. Test d'existence

- III-C. Test de position

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

- IV. Parenthèses et contexte

- V. Quelques exemples combinés

- V-A. Un nœud avec au moins N fils

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

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

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

- V-E. Suppression de doublons

I - 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.

II - Opérateurs

II-A - Les opérateurs booléens

Les booléens sont true() et false(). Le nœud 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 : <=, <, >=, >.

II-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.

III - Test

Lors de test entraînant une comparaison tout nœud 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.

III-A - Test de valeur

Il porte sur la valeur textuelle du nœud 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>

III-B - Test d'existence

On souhaite sélectionner un nœud en fonction de ses fils/père/attributs/etc. Dans ce cas, le XPath présent dans le prédicat sera évalué à partir du nœud 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.

III-C - Test de position

On utilisera pour ceci une fonction XPath.

La fonction position() renvoie la position du nœud 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 nœud parent.
Ainsi un XPath du type //*[2] ne renvoie pas le deuxième nœud de la sélection, mais tous les nœuds 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>

III-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 nœuds via = , l'expression renvoie vrai si la valeur textuelle d'un des nœuds 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 nœuds via != , l'expression renvoie vrai si la valeur textuelle d'un des nœuds 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>

IV - Parenthèses et contexte

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

À 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 nœuds sans contexte.

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

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>

V - 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.

V-A - Un nœud avec au moins N fils

Si on souhaite un nœud 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>

V-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>

V-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>

V-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>

V-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>