2010-05-20 3 views
2

J'ai des difficultés à obtenir xsl:sort pour comprendre la portée des attributs auxquels je fais référence. Voici un exemple de document XML pour illustrer:En XSLT, comment trier en utilisant une clé indirecte?

<Root> 
    <DrinkSelections> 
    <Drink id=1000 name="Coffee"/> 
    <Drink id=1001 name="Water"/> 
    <Drink id=1002 name="Tea"/> 
    <Drink id=1003 name="Almost But Not Quite Entirely Unlike Tea"/> 
    </DrinkSelections> 

    <CustomerOrder> 
    <Drinks> 
     <Drink oid="1001"/> 
     <Drink oid="1002"/> 
     <Drink oid="1003"/> 
    </Drinks> 
    </CustomerOrder 

</Root> 

Je veux produire une liste de boissons (triées par nom) contenu dans le CustomerOrder. Voici le code XSLT je tripotent:

<xsl:for-each select="/Root/CustomerOrder/Drinks/Drink"> 
    <xsl:sort select="/Root/DrinkSelections/Drink[@id = @oid]/@name"/> 
    <xsl:variable name=var_oid select="@oid"/> 
    <xsl:value-of select="/Root/DrinkSelections/Drink[@id = $var_oid]/@name"/> 
</xsl:for-each> 

Apparemment, la commande xsl:sort tente d'appliquer l'attribut « oid » aux éléments de verre dans DrinkSelections, plutôt que l'élément local boisson.

Je peux contourner cela en utilisant une variable, comme dans l'instruction xsl:value-of. Mais puisque xsl:sort doit être la première déclaration après l'instruction xsl:for-each, je ne peux pas insérer l'instruction xsl:variable avant xsl:sort.

Existe-t-il un moyen d'indiquer explicitement que la valeur d'attribut doit être prise de l'élément "local"?

Répondre

2

Il vous manque la fonction current().

<xsl:for-each select="/Root/CustomerOrder/Drinks/Drink"> 
    <xsl:sort select="/Root/DrinkSelections/Drink[@id = current()/@oid]/@name"/> 
    <xsl:value-of select="/Root/DrinkSelections/Drink[@id = current()/@oid]/@name"/> 
</xsl:for-each> 

Mais une place plus importante, il vous manque des clés XSL, pour une meilleure lisibilité et performance:

<xsl:key name="kDrinkById" match="DrinkSelections/Drink" use="@id" /> 

<!-- ... later ... --> 
<xsl:for-each select="/Root/CustomerOrder/Drinks/Drink"> 
    <xsl:sort select="key('kDrinkById', @oid)/@name"/> 
    <xsl:value-of select="key('kDrinkById', @oid)/@name"/> 
</xsl:for-each> 

Un vous n'êtes probablement pas en utilisant des modèles de droite, parce que si vous avez fait, votre xsl:for-each expression de sélection serait pas commencer à la racine.

<xsl:template match="Root"> 
    <xsl:apply-templates select="CustomerOrder/Drinks" /> 
</xsl:template> 

<xsl:template match="CustomerOrder/Drinks"> 
    <xsl:apply-templates select="Drink"> 
    <xsl:sort select="key('kDrinkById', @oid)/@name"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="CustomerOrder/Drinks/Drink"> 
    <xsl:value-of select="key('kDrinkById', @oid)/@name"/> 
</xsl:template> 

Notez que j'ai également supprimé le pour-chaque. Chaque xsl:for-each évité est une étape vers un meilleur code XSLT (de très rares exceptions s'appliquent).

+0

Merci! Je pensais qu'il y avait une référence explicite disponible. Et merci aussi pour l'approche en utilisant une clé. Je n'ai que quelques fois joué avec les touches dans mon expérience XSLT quelque peu limitée. Et oui, je ne commence pas pour-chacun de la racine. J'essayais juste de rendre l'exemple plus concis. Mon code utilise une référence relative dans un modèle de correspondance. –

+0

@edholder: Je le pensais, mais on ne sait jamais. ;-) La clé accélérera grandement les choses si vous avez de grandes quantités de données à traiter de cette façon. – Tomalak

Questions connexes