Technologies internet


XSLT - filtrage de doublons


Voir :


Soit le document suivant :
<?xml version="1.0" encoding="UTF-8"?>
<contacts>
  <contact>
    <prenom>Jean</prenom>
    <nom>Chenier</nom>
  </contact>
  <contact>
    <prenom>Marie</prenom>
    <nom>Pariseau</nom>
  </contact>
  <contact>
    <prenom>Jean</prenom>
    <nom>Poirier</nom>
  </contact>
</contacts>
On souhaite afficher la liste de toutes les personnes dans des tableaux, chaque tableau regroupant les contacts ayant un même prénom.

Méthode "classique"

Pour sélectionner tous les prénoms, nous écrivons la requête XPath suivante :
/contacts/contact/prenom
Le résultat contiendra deux personnes ayant le prénom Jean. Pour identifier les tableaux et leur nombre, nous devons filtrer les doublons.

Nous ajoutons une condition sur l'élément "contact" pour exprimer le fait que l'on ignore le contact si une personne avec le prénom du contact a été déjà rencontrée.

not(preceding::contact/prenom = prenom)
Ce test utilise l'opérateur XPath "=" pour comparer deux ensembles de noeuds: l'ensemble de tous les sous éléments "prenom" des éléments "contact" précédents et l'ensemble composé de l'élément "prenom" de l'élément "contact" courant. Pour XPath, ce test est vrai si les deux ensembles ont au moins un noeud ayant une valeur commune.

La condition entière est vraie si les deux ensembles n'ont pas d'élément commun. Nous pouvons l'utiliser dans les attributs "select" ou "match" :

<xsl:apply-templates select="/contacts/contact[not( preceding::contact/prenom = prenom )]"/>
ou :
<xsl:variable name="prenoms-distincts" select="/contacts/contact[not( preceding::contact/prenom = prenom )]"/>

Méthode "muenchian"

La méthode que nous venons d'exposer demande au processeur XSLT de rechercher de manière répétitive dans tous les éléments "contact" précédents s'il y en a un de même prénom que l'élément courant.

Cette recherche peut devenir longue lorsque la taille du document est importante.

La méthode connue sous le nom de méthode "muenchian" a été proposée pour optimiser le filtrage des doublons par Steve Muench d'Oracle.

Elle repose sur l'utilisation des clés XSLT (xsl:key).

Les clés XSLT permettent l'accès rapide à des ensembles de noeuds. Dans notre cas, nous pouvons définir une clé sur les éléments "contact" en utilisant leur sous-élément "prenom" :

<xsl:key name="id" match="contact" use="prenom"/>
Cette déclaration définit une clé dont le nom est "id", qui porte sur tous les éléments "contact" du document et qui utilise comme identifiant la valeur de leur sous-élément "prenom".

Pour sélectionner la liste des éléments "contact" ayant pour prenom "Jean", nous pouvons maintenant écrire l'expression XPath :

key('id', 'Jean')
Cet accès par clé est optimisé par les processeurs XSLT qui construisent une table de correspondance à la première utilisation de la clé. Cette première utilisation est donc plus lente qu'un de nos balayages répétitifs, mais les utilisations suivantes utilisent la table de correspondance et elles sont beaucoup plus rapides.

Exemple:

<xsl:apply-templates select="key('id', 'Jean')"/>
traite tous les éléments "contact" ayant 'Jean' comme prénom et
<xsl:apply-templates select="key('id', 'Jean')[2]"/>
traitera le deuxième "contact" (dans notre cas, celui de Jean Poirier).

Filtrage des doublons à l'aide d'une clé XSLT

On peut utiliser la clé générée en spécifiant que l'on veut sélectionner les éléments "contact" qui sont les premiers de la liste des éléments qui ont le même nom.

Nous avons deux possibilités pour tester que ces deux expressions correspondent au même noeud et ces deux possibilités vont nous fournir deux variantes de la méthode "muenchian". La première possibilité est de tester si les identifiants obtenus par la fonction XSLT "generate-id()" sont égaux.

La fonction generate-id(node-set) retourne une chaîne de caractères qui identifie d'une manière unique le noeud dans l'ensemble de noeuds reçu en argument qui est le premier selon l'ordre du document.

La condition suivante sera donc vraie:

generate-id(key('id',prenom)) = generate-id(key('id',prenom)[1])
Nous pouvons écrire alors :
/contacts/contact[generate-id(.) = generate-id(key('id', prenom)[1])]
Si cette condition vous semble être assez complexe, regardez le tableau avec les valeurs exemplaires :
contactgenerate-id(.)generate-id(key('id',prenom))
Jean Chenier'idcontact86585840''idcontact86585840'
Marie Pariseau'idcontact86350544''idcontact86350544'
Jean Poirier'idcontact88974024''idcontact86585840'
La deuxième possibilité est de tester que l'ensemble des noeuds constitué par le noeud courant et le premier élément de la liste des éléments de même nom ne comporte qu'un seul élément :
/contacts/contact/prenom[ count( . | key('id', prenom)[1]) = 1]
Les deux variantes donnent strictement le même résultat et sont équivalentes en terme de performance.

Dans les deux cas, ces expressions XPath/XSLT peuvent être utilisées dans une instruction XSLT telle que :

<xsl:apply-templates select="/contacts/contact/prenom[generate-id() = generate-id(key('id', prenom)[1])]"/>
ou encore :
<xsl:variable name="prenoms-distincts" select="/contacts/contact[ count( . | key('id', prenom)[1]) = 1]"/>

Avantages et inconvénients de la méthode "muenchian"

XPath/XSLT 2.0?