2010-02-18 6 views
46

je XML comme ceci:Comment utiliser XSLT pour créer des valeurs distinctes

<items> 
    <item> 
    <products> 
     <product>laptop</product> 
     <product>charger</product> 
    </products> 
    </item> 
    <item> 
    <products> 
     <product>laptop</product> 
     <product>headphones</product> 
    </products> 
    </item> 
</items> 

Je veux à la sortie comme

 
laptop 
charger 
headphones 

Je tentais d'utiliser distinct-values() mais je suppose im faire quelque chose de mal . Quelqu'un peut-il me dire comment y parvenir en utilisant distinct-values()? Merci.

<xsl:template match="/">    
    <xsl:for-each select="//products/product/text()"> 
    <li> 
     <xsl:value-of select="distinct-values(.)"/> 
    </li>    
    </xsl:for-each> 
</xsl:template> 

mais sa me donner une sortie comme ceci:

<li>laptop</li> 
<li>charger</li> 
<li>laptop></li> 
<li>headphones</li> 
+0

vaudraient peut-être examiner cette question connexe: http://stackoverflow.com/questions/1813286/xslt-select-distinct-but-slightly-different-to-other-examples –

Répondre

47

Une solution XSLT 1,0 qui utilise key et la fonction generate-id() pour obtenir des valeurs distinctes:

<?xml version="1.0" encoding="UTF-8"?> 
    <xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/> 

<xsl:key name="product" match="/items/item/products/product/text()" use="." /> 

<xsl:template match="/"> 

    <xsl:for-each select="/items/item/products/product/text()[generate-id() 
             = generate-id(key('product',.)[1])]"> 
    <li> 
     <xsl:value-of select="."/> 
    </li> 
    </xsl:for-each> 

</xsl:template> 

</xsl:stylesheet> 
+0

Ceci fonctionne parfaitement _only_ pour trouver des éléments distincts sous l'espace/namespace entier. Si le but est de trouver des sous-arborescences distinctes comme dans alors l'approche par clé globale n'est plus valide ... –

+0

@R. Simac - vous pouvez ajuster l'expression de correspondance de la clé pour qu'elle corresponde à un ensemble d'éléments différent. –

+0

@Mads, je pensais être incapable de définir la correspondance de clé «dynamiquement». Je ne sais pas comment utiliser/instruire la clé pour faire correspondre uniquement les éléments distincts sous l'entrepôt X ... –

7

distinct-values(//product/text())

+0

@Tomalak, « exponentielle "? Non, uniquement * linear * dans le nombre de nœuds d'élément et de nœuds feuille de type quelconque dans le document XML. –

+0

Je n'arrive pas à faire marcher ça, mon compilateur (eclipse) se plaint que c'est XPath invalide. – Nicholas

+2

@Nicholas Ceci est pour XSLT 2.0 mais vous travaillez avec un processeur XSLT 1.0. Vous devez utiliser un '', comme le fait la [réponse acceptée] (http://stackoverflow.com/a/2293626/18771). – Tomalak

14

Vous ne voulez pas "sortie (valeurs distinctes)", mais plutôt « for-each (distinct- valeurs) ":

<xsl:template match="/">    
    <xsl:for-each select="distinct-values(/items/item/products/product/text())"> 
    <li> 
     <xsl:value-of select="."/> 
    </li> 
    </xsl:for-each> 
</xsl:template> 
+0

Son fonctionnement bien ... il y avait un bug dans mon xml .... merci beaucoup ... –

+0

Tomalak xslt 2.0 n'est pas pris en charge par le navigateur ... vient de savoir ... tout en testant ... de toute façon pour le faire sans xslt 2.0 –

+0

@AB - J'ai ajouté une solution XSLT 1.0 –

45

est ici une solution XSLT 1.0 que je l'ai utilisé dans le passé, je pense qu'il est plus succinct (et lisible) que d'utiliser la fonction generate-id().

<xsl:template match="/">   
    <ul> 
     <xsl:for-each select="//products/product[not(.=preceding::*)]"> 
     <li> 
      <xsl:value-of select="."/> 
     </li> 
     </xsl:for-each>    
    </ul> 
    </xsl:template> 

Retours:

<ul xmlns="http://www.w3.org/1999/xhtml"> 
    <li>laptop</li> 
    <li>charger</li> 
    <li>headphones</li> 
</ul> 
+0

Tout en appréciant la réponse ci-dessus qui est complètement applicable pour le cas d'origine, je voulais juste noter que l'approche ci-dessus ne s'applique pas pour un schéma légèrement plus complexe, où chaque produit a ses propres éléments , par exemple: chargeur ordinateur portable ... Je n'ai pas pu trouver des noms distincts pour une telle mise en page, peut-être atteint les limites de xslt1.0 ici ... –

+0

vraiment @ R.Simac? Le xpath suivant devrait vous donner les produits, avec la première instance d'un nom (si c'est ce que vous voulez?) ... '// product [not (./ name = previous :: */name)]'. Je crois que cela peut ne pas fonctionner pour tous les scénarios, peut-être pouvez-vous donner un exemple où cela ne fonctionne pas? –

+0

@NickG ... c'était l'une de ces situations "ça ne marche pas pour moi (tm) ... Par exemple, votre suggestion ne produit pas de sortie pour xml suivant (désolé pour le formatage laid, temps limité): ordinateur portable chargeur ordinateur portable casque Chargeur

11

Je suis venu à ce problème tout en travaillant avec un rendu XSL Sitecore. L'approche qui a utilisé key() et l'approche qui a utilisé l'axe précédent a été très lente. J'ai fini par utiliser une méthode similaire à key() mais cela n'a pas nécessité d'utiliser key(). Il effectue très rapidement.

<xsl:variable name="prods" select="items/item/products/product" /> 
<xsl:for-each select="$prods"> 
    <xsl:if test="generate-id() = generate-id($prods[. = current()][1])"> 
    <xsl:value-of select="." /> 
    <br /> 
    </xsl:if> 
</xsl:for-each> 
+1

Cela a fonctionné pour moi. C'était agréable de pouvoir garder les choses bien encapsulées dans la feuille de style. Ajouter à la place de la valeur-de m'a permis d'appliquer le modèle uniquement au nœud spécifique. – kjl

+0

Fonctionne pour moi! Upvote donné. – Naha

Questions connexes