*
correspond à zero, une ou plusieurs occurrences, +
correspond à au moins une occurrence et ?
correspond à au plus une occurrence
Une expression XPath est divisée en étapes séparées par /
ou //
. //
signifie que l'on peut descendre arbirairement profondément dans le document avant de continuer l'évaluation de l'expression.
Dans la syntaxe non abrégée, on spécifie les selections par axe::test
. L'axe exprime la manière dont on poursuit l'exploration du document. Le test agit comme un filtre sur les noeuds considérés.
child::
Enfant de l'élément sélectionné
descendant::
Descendant (i.e. on peut descendre autant que l'on veut)
attribute::
Attribut de l'élément actuellement séléectionné
self::
Selection du noeud courant: pas de déplacement.
descendant-or-self::
Auto explicatif (i.e. descendant ou self)
following-sibling::
Noeuds au même niveau que le noeud courant et après le noeud courant.
following::
Noeuds après le noeud courant, mais pas forcément au même niveau.
parent::
Noeud parent du noeud courant
ancestor::
Noeuds ancêtres du noeud courant
preceding-sibling::
Noeuds avant le noeud courant, et au même niveau
preceding::
Noeuds avant le noeud courant, pas forcément au même niveau
ancestor-or-self::
Auto explicatif
nom
Sélectionne les élément dont le nom est nom
*
N'importe quel élément, mais pas le texte
element()
Un élément. Le nom peut être donné entre partenthèses.
node()
N'importe quel noeud, y compris le texte et les attributs.
text()
Du texte
attribute()
Un attribut. Le nom peut être donné entre parenthèses
document-node()
La racine du document
processing-instruction()
Une commande
comment()
Un commentaire
Exemple:
Dans le document
<bib> <book title="a"/> <book title="b"> <critique> C'est un livre très intéressant </critique> </book> </bib>
child::bib
le noeud bib
en entier (car l'élément le plus à l'extérieur est considéré comme étant le fils de la racine).child::bib/child::book
selectionne les deux éléments <book title="a"/>
et <book title="b"> …… </book >
//child::book/attribute::title
sélectionne les attributs title="a"
et title="b"
//self::critique/child::text()
sélectionne le texte “C'est un livre très intéressant
”.
Équivalent à child::nom_element
Exemple:
Dans le document
<bib> <book title="a"/> <book title="b"/> </bib>
bib/book
selectionne les deux enfants <book title=“a”/>
et <book title=“b”/>
Selectionne l'attribut donné.
Exemple:
Dans le document
<bib> <book title="a" year="1999"/> <book title="b"/> </bib>
bib/book/@year
selectionne l'attribut year=“1999”
Équivalent à parent::* .
Exemple:
Dans le document
<categorie nom="tout"> <categorie nom="film"> <categorie nom="comique"><film titre="Les ripoux"/></categorie> </categorie> </categorie>
/descendant::film/..
selectionne <categorie nom=“comique”><film titre=“Les ripoux”/></categorie>
.
Équivalent à /descendant-or-self::*/
Exemple:
Dans le document
<categorie nom="tout"> <categorie nom="film"> <categorie nom="comique"><film titre="Les ripoux"/></categorie> </categorie> </categorie>
//film
séléctionne <film titre=“Les ripoux”/>
.
Il peuvent suivre une sélection par axe::test
afin de filtrer les éléments sélectionnés.
Ils sont le la forme [expr]
. On peut en mettre autant que l'on souhaite.
Ils sélectionnent les élements vérifiant:
expr
indique un élément ou un attribut, celui-ci doit être présent (i.e. contenu dans l'élément sélectionné).expr
s'évalue à un entier n, seuls les noeuds à la position n sont sélectionnés.expr
est une expression booléenne qui doit s'évaluer à vrai.Exemple:
Dans le document
<bib> <book title="a" year="1999"/> <book title="b"/> </bib>
bib/book[@year]
sélectionne <book title=“a” year=“1999”/>
bib/book[@title = 'b']
sélectionne <book title=“b”/>
bib/book[2]
selectionne <book title=“b”/>
bib/book[2]/attribute::*
sélectionne l'attribut title=“b”
Xquery permet de construire des documents à partir de requêtes XPath.
Un nom de variable est précédé d'un $
(ex: $a
).
Une expression XPath est aussi une expression XQuery. Le résultat de cette expression est l'ensemble des noeuds et/ou attributs sélectionnés par l'expression XPath.
Il est possible de produire un document XML à partir d'un programme XQuery en écrivant le document XML dans le programme avec des parties du document représentées par des expressions XQuery entre accolades { }. Si le résultat d'une expression est un (ou plusieurs) attribut, celui-ci sera intégré dans l'élément englobant l'expression.
Exemple:
Dans le document
<bib> <book title="a" year="1999"/> <book title="b"/> </bib>
<results>{ bib/book }</results>
produit:<results> <book title="a" year="1999"/> <book title="b"/> </results>
<result>{bib/book/@year}</result>
produit:<result year="1999"/>
On remarque que l'expression XPath sélectionne un attribut, year. Comme l'expression est positionnée comme un enfant de l'élément <result>
, son résultat est un enfant de cet élément. Comme ce résultat est un attribut, il est intégré à l'élément.
for $v1 in expr1, $v2 in expr2 ... let $v3 := expr3, $v4 := expr4 ... where expr_bool order by expr_a, expr_b return expr
Par la suite on considérera que les documents interrogés correspondent à la DTD suivante:
<!ELEMENT collection (artist*)> <!ELEMENT artist (name,album+)> <!ELEMENT name (#PCDATA)> <!ELEMENT album (title,track+)> <!ELEMENT title (#PCDATA)> <!ELEMENT track (title)> <!ATTLIST track seconds CDATA #REQUIRED>
Exemple:
L'expression suivante va, pour chaque artiste, donner son nom dans une balise nom.
for $n in //artist/name return <nom>{$n/text()}</nom>
Exemple:
Ici on crée, pour chaque artiste, pour chaque élément track de cet artiste, un élément chanson dans lequel on précise le titre et l'artiste.
for $a in //artist, $t in $a//track return <chanson> <artiste>{$a/name/text()}</artiste> <titre>{$t/title/text()}</titre> </chanson>
Exemple:
La même que ci-dessus, mais en ordonnant le résultat par titre. On utilise pour cela une variable $titre définie pour chaque valeur des variables $a et $t.
for $a in //artist, $t in $a//track let $titre := $t/title/text() order by $titre return <chanson> <artiste>{$a/name/text()}</artiste> <titre>{$titre}</titre> </chanson>
Exemple:
Enfin on suppose que l'on est intéressé que par des chansons dont la durée est comprise entre 2 et 4 minutes. On précise en plus cette durée dans un attribut valeur, situé dans un élément duree.
for $a in //artist, $t in $a//track let $titre := $t/title/text(), $duree := $t/@seconds where $duree >= 120 and $duree <= 240 order by $titre return <chanson> <artiste>{$a/name/text()}</artiste> <titre>{$titre}</titre> <duree valeur="{$duree}"/> </chanson>
Il est possible de définir des fonctions dans XQuery de la manière suivante:
declare function nom ($arg1 as type1, $arg2 as type2, ...) as type_retour { corps de la fonction };
Le nom de la fonction doit être préfixé. Il existe un préfixe créé par défaut: local.
Les types peuvent être:
text()
, comment()
, processing-instruction()
element()
, attribute()
: le nom peut être spécifié entre les parenthèsesxs:type
, où type est un type défini dans la norme XML Schema, par exemple xs:string
, xs:boolean
, xs:number
, xs:integer
.
Un type peut être suivit de *
, +
ou ?
afin d'exprimer le fait de pouvoir gérer une séquence d'éléments au lieu d'un seul élément1).
Les déclarations de type sont facultatives.
Il faut également noter qu'une fonction ne possède pas point de départ dans l'arbre XML. Il faut donc lui passer un argument qui servira de point de départ lorsque cela est nécessaire.
Exemple:
declare function local:possede_titre($art as element(artist),$song as xs:string) { $art/album[track/title=$song] };
La fonction s'utilise ensuite de manière classique:
local:possede_titre(//artist,'b')
XQuery est un langage fonctionnel et un programme XQuery est donc tout d'abord une expression. Cette expression peut être précédée d'un certain nombre de déclarations, en particulier des déclarations de fonctions. Parmi les autres déclarations possibles, on peut trouver:
declare namespace nomprefixe="uri_espace_nommage";
Ces déclarations sont similaires en fonctionnalité aux déclarations d'espace de nommage dans les documents XML (voir ici).
declare default element namespace "uri_espace_nommage"; declare default function namespace "uri_espace_nommage";
declare variable $nom_avec_prefixe as type := valeur; declare variable $nom_avec_prefixe := valeur; declare variable $nom_avec_prefixe as type external; declare variable $nom_avec_prefixe external;
import module namespace nom_prefixe="uri_namespace_module" at "uri_emplacement_module"; import module namespace nom_prefixe="uri_namespace_module"; import module namespace "uri_namespace_module" at "uri_emplacement_module"; import module namespace "uri_namespace_module";
Exemple:
Le programme xquery suivant extrait les artistes d'une collection prise dans un document xml récupéré sur le web et fabrique une page HTML qui les affiche avec la liste de leurs albums:
(: déclaration de version xquery, avec l'encodage du fichier :) xquery version "1.0" encoding "utf-8"; (: les éléments pour pour espace de nommage par défaut celui correspondant à XHTML :) declare default element namespace "http://www.w3.org/1999/xhtml"; (: Le préfix col correspond à l'espace de nommage de la collection :) declare namespace col="http://ecoquery.univ-lyon1.fr/collection-cd"; (: La fonction prend un élement artist et produit deux élements: - un élément <dt> avec le nom de l'artiste - un élement <dd> avec la liste des albums La valeur retournée par la fonction est donc une suite d'élements que l'on va séparer par une virgule. Pour bien différencier chaque constituant de la séquence, on les met entre parenthèses (mais c'est optionnel). :) declare function local:presente-artiste($art) { (: Premier élement: le nom de l'artiste :) (<dt><strong>{$art/col:name/text()}</strong></dt>), (: deuxieme élément: la liste des titres d'albums, générée via une boucle for :) ( <dd><ul> { for $album in $art/col:album return <li>{$album/col:title/text()}</li> } </ul></dd> ) }; (: Le document principal :) <html> <head><title>Artistes</title></head> <body> <h1>Artistes</h1> <dl> { (: La fonction doc est prédéfinie dans XPath et renvoie le document dont l'url est passée en argument. Ici, on va donc chercher tous les noeud artist dans l'espace de nommage "http://ecoquery.univ-lyon1.fr/collection-cd" présents dans le document accessible à http://liris.cnrs.fr/~ecoquery/files/artistes.xml:) for $artiste in doc("http://liris.cnrs.fr/~ecoquery/files/artistes.xml")//col:artist return local:presente-artiste($artiste) } </dl> </body> </html>
Le résultat de l'exécution est:
<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Artistes</title> </head> <body> <h1>Artistes</h1> <dl> <dt> <strong>Phil Collins</strong> </dt> <dd> <ul> <li>Face Value</li> <li>Both sides</li> </ul> </dd> <dt> <strong>Genesis</strong> </dt> <dd> <ul> <li>The Lamb Lies Down on Broadway</li> </ul> </dd> <dt> <strong>Jean-Jacques Goldman</strong> </dt> <dd> <ul> <li>Non homologué</li> </ul> </dd> </dl> </body> </html>
Un module XQuery est une suite de déclarations, mais sans expression à la fin. Pour être valable, un module doit contenir la déclaration suivante:
module namespace nom_prefixe_module = "uri_du_module";
Exemple:
Module contenant la fonction du programme précédent:
xquery version "1.0" encoding "utf-8"; (: déclaration de module :) module namespace monmodule="http://ecoquery.univ-lyon1.fr/mon_mod"; declare default element namespace "http://www.w3.org/1999/xhtml"; declare namespace col="http://ecoquery.univ-lyon1.fr/collection-cd"; (: le préfixe n'est plus local, mais monmodule :) declare function monmodule:presente-artiste($art) { (<dt><strong>{$art/col:name/text()}</strong></dt>), ( <dd><ul> { for $album in $art/col:album return <li>{$album/col:title/text()}</li> } </ul></dd> ) };
Programme faisant appel au module:
xquery version "1.0" encoding "utf-8"; declare default element namespace "http://www.w3.org/1999/xhtml"; declare namespace col="http://ecoquery.univ-lyon1.fr/collection-cd"; (: Import du module, le prefixe n'est pas le même que dans le fichier du module, mais c'est l'espace de nommage qui compte :) import module namespace pres="http://ecoquery.univ-lyon1.fr/mon_mod" at "module-exemple2.xqm"; (: Le document principal :) <html> <head><title>Artistes</title></head> <body> <h1>Artistes</h1> <dl> { for $artiste in doc("http://liris.cnrs.fr/~ecoquery/files/artistes.xml")//col:artist (: le préfixe de la fonction a changé :) return pres:presente-artiste($artiste) } </dl> </body> </html>
Page du W3C sur XQuery/XPath:
*
correspond à zero, une ou plusieurs occurrences, +
correspond à au moins une occurrence et ?
correspond à au plus une occurrence