2010-06-10 6 views
5

Je dois sélectionner uniquement des enregistrements uniques à partir d'un document XML, dans le contexte d'une boucle <xsl:for-each>. Je suis limité par Visual Studio à l'aide de XSL 1.0.Sélection d'enregistrements uniques dans XSLT/XPath

<availList> 
     <item> 
      <schDate>2010-06-24</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>13:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-24</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>13:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-25</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>12:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-26</schDate>    
      <schFrmTime>13:00:00</schFrmTime> 
      <schToTime>14:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-26</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>12:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
    </availList> 

L'unicité doit être fondée sur la valeur des trois éléments de l'enfant: schDate, schFrmTime et schToTime. Si deux éléments item ont les mêmes valeurs pour les trois éléments enfants, ils sont des doublons. Dans le XML ci-dessus, les éléments un et deux sont des doublons. Le reste est unique. Comme indiqué ci-dessus, chaque élément contient d'autres éléments que nous ne souhaitons pas inclure dans la comparaison. "Unicité" devrait être un facteur de ces trois éléments, et ceux-là seuls.

J'ai essayé d'y arriver par les moyens suivants:

availList/item[not(schDate = preceding:: schDate and schFrmTime = preceding:: schFrmTime and schToTime = preceding:: schToTime)] 

L'idée est de sélectionner les enregistrements où il n'y a aucun élément précédent avec le même schDate, schFrmTime et schToTime. Cependant, sa sortie est manquant le dernier article. C'est parce que ma XPath est en fait en train d'exclure les éléments où toutes les valeurs de l'élément enfant correspondent dans tout le document précédent. Aucun item ne correspond à tous les éléments enfants du dernier élément - mais comme la valeur de chaque élément est individuellement présente dans un autre élément, le dernier élément est exclu.

je pourrais obtenir le résultat correct en comparant toutes les valeurs de l'enfant en tant que chaîne concaténée à les mêmes valeurs concaténées pour chaque élément précédent. Est-ce que quelqu'un sait d'une manière que je pourrais faire ceci?

+0

Bonne question (+1). Voir ma réponse pour une XPath et une solution XSLT. –

+1

La méthode utilisant key() est communément appelée méthode Muenchian: http://www.jenitennison.com/xslt/grouping/muenchian.html –

Répondre

4

I. Comme une seule expression XPath:

/*/item[normalize-space() and not(. = preceding-sibling::item)] 

II.mise en œuvre plus efficaces (XSLT), en utilisant les touches:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:key name="kItemByVal" match="item" use="."/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "*/item[generate-id() = generate-id(key('kItemByVal', .))] 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

deux I et II, lorsqu'elle est appliquée sur le document XML fourni sélectionner correctement/copier les nœuds suivants:

<item><schDate>2010-06-24</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>13:00:00</schToTime></item> 
<item><schDate>2010-06-25</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item> 
<item><schDate>2010-06-26</schDate><schFrmTime>13:00:00</schFrmTime><schToTime>14:00:00</schToTime></item> 
<item><schDate>2010-06-26</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item> 

Mise à jour : Dans le cas <item> a d'autres enfants, alors cette transformation:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

    <xsl:key name="kItemBy3Children" match="item" 
    use="concat(schDate, '+', schFrmTime, '+', schToTime)"/> 

<xsl:template match="/"> 
     <xsl:copy-of select= 
     "*/item[generate-id() 
       = generate-id(key('kItemBy3Children', 
           concat(schDate, 
             '+', schFrmTime, 
             '+', schToTime) 
           ) 
          ) 
       ] 
     "/> 
</xsl:template> 
</xsl:stylesheet> 

produit le résultat souhaité.

+0

Dimitre, Merci beaucoup pour votre réponse. Je crains que cela ne fonctionne pas pour mon cas, cependant - je m'excuse de ne pas avoir été très clair lorsque j'ai écrit ma question (je l'ai ensuite édité). Le problème est que, en réalité, mes éléments 'item' contiennent aussi divers autres sous-éléments qui ne doivent pas être pris en compte pour savoir si les éléments sont sélectionnés ou non. Je ne cherche pas réellement l'unicité «réelle», je cherche l'unicité seulement dans certaines valeurs d'éléments enfants. Je suis sûr que votre réponse sera précieuse pour les autres, cependant. Dan –

+1

@ Daniel-I-S: J'ai mis à jour ma réponse avec une solution au problème modifié. –

+2

C'est une excellente réponse. Merci beaucoup. –

2

La technique que j'ai vu est de le faire en deux passes: trier les éléments par les trois champs clés, puis comparer chaque élément à son élément précédent (au lieu de tous les éléments précédents).

Est-il pratique pour vous d'exécuter deux transformations distinctes? Cela rend le problème beaucoup plus facile.

J'ai vu la technique dans une ancienne édition de Michael Kay's XSLT book. Vous pourriez le trouver dans certains de ses exemples de code là-bas.

Questions connexes