Résumé XPath/XQuery

XPath

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.

Syntaxe non abrégée

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.

Axes

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

Tests

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

Syntaxe abrégée

nom_element

É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”/>

@attribut

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”/>.

Prédicats

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:

  • Si expr indique un élément ou un attribut, celui-ci doit être présent (i.e. contenu dans l'élément sélectionné).
  • Si expr s'évalue à un entier n, seuls les noeuds à la position n sont sélectionnés.
  • Sinon 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

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.


Expressions FLWOR

Syntaxe:

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>

Définition de fonctions

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èses
  • xs: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')

Programmes XQuery

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:

  • Des déclarations de préfixes rattachés à un espace de nommage:
    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).

  • Déclaration d'espace de nommage par défaut pour les éléments et/ou les fonctions:
    declare default element namespace "uri_espace_nommage";
    declare default function namespace "uri_espace_nommage";
  • Des déclarations de variables (avec ou sans type, avec fournie par le programme ou par l'environnement):
    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;
  • Des imports de modules (voir ci-dessous):
    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>

Modules XQuery

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>

Liens

1)
* correspond à zero, une ou plusieurs occurrences, + correspond à au moins une occurrence et ? correspond à au plus une occurrence