2010-10-07 21 views
9

Ho una chiamata ragionevolmente complesso da xsl: apply-templates:Come utilizzare la variabile XSL in xsl: apply-templates?

<xsl:apply-templates select="columnval[@id 
             and not(@id='_Name_') 
             and not(@id='Group') 
             and not(@id='_Count_')]"/> 

L'espressione viene riutilizzato in altri posti come questo:

<xsl:apply-templates select="someothernode[@id 
              and not(@id='_Name_') 
              and not(@id='Group') 
              and not(@id='_Count_')]"/> 

voglio generalizzare in qualche modo, così posso definire una volta e riutilizzalo altrove. Tuttavia, questo non sembra funzionare:

<xsl:variable name="x">@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')</xsl:variable> 
<xsl:apply-templates select="columnval[$x]"/> 
<xsl:apply-templates select="someothernode[$x]"/> 

C'è un modo migliore/diverso di farlo? Tutto quello che voglio è riutilizzare l'espressione xpath in più chiamate a xsl: apply-templates (alcuni dei quali selezionano da diversi bambini).

Questo verrà utilizzato in un'applicazione client, quindi non è possibile utilizzare estensioni o passare a XSLT 2 sfortunatamente. :(

Grazie.

+0

Buona domanda. Vedere la mia risposta per una descrizione di due possibili soluzioni (XSLT 1.0 e XSLT 2.0) e un suggerimento di una soluzione più potente utilizzando le funzioni di ordine superiore. –

risposta

5

Non è possibile costruire XPath in modo dinamico in XSLT (almeno, non XSLT 1.0). Ma si può facilmente realizzare quello che stai cercando di fare con le modalità di modello:

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-template select="someothernode" mode="filter"/> 

... 

<!-- this guarantees that elements that don't match the filter don't get output --> 
<xsl:template match="*" mode="filter"/> 

<xsl:template match="*[@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')]" mode="filter"> 
    <xsl:apply-templates select="." mode="filtered"/> 
</xsl:template> 

<xsl:template match="columnval" mode="filtered"> 
    <!-- this will only be applied to the columnval elements that pass the filter --> 
</xsl:template> 

<xsl:template match="someothernode" mode="filtered"> 
    <!-- this will only be applied to the someothernode elements that pass the filter --> 
</xsl:template> 
+0

+1 questo è un approccio efficiente.Un unico svantaggio è che il filtro è hardcoded e quindi non variabile.Se un filtro variabile non è necessario, vorrei andare con questa soluzione. – Tomalak

+0

Modelli di modalità non aveva senso per me fino a quando non ho iniziato a imbattersi in problemi come gli OP. –

1

vorrei prendere un'occhiata a utilizzare una proroga per XSLT. Io non credo che si può fare in XSLT "standard".

Questa estensione può fare quello che vuoi : http://www.exslt.org/dyn/functions/evaluate/index.html

+0

Domanda aggiornata: non possiamo usare le estensioni, poiché ci stiamo affidando a MSXML per fare la trasformazione :( – Colen

+0

'msxsl: node-set' funzionerà, quindi. –

1

Con l'exsl estensione: serie di nodi, è possibile creare un modello di nome che accetta una serie di nodi $ x e restituisce il filtrato serie di nodi in base al predicato statica

È inoltre possibile definire una funzione, in XSLT 2.0.

+0

Domanda aggiornata - sfortunatamente siamo bloccati con XSLT 1.0.: ( – Colen

1

ne dite:

<xsl:variable name="filter" select="_Name_|Group|_Count_" /> 

<xsl:apply-templates select="columnval" mode="filtered" /> 
<xsl:apply-templates select="someothernode" mode="filtered" /> 

<xsl:template match="someothernode|columnval" mode="filtered"> 
    <xsl:if test="not(contains(
    concat('|', $filter,'|'), 
    concat('|', @id,'|'), 
))"> 
    <!-- whatever --> 
    </xsl:if> 
</xsl:template> 

Si potrebbe fare un $filter param, e passarlo dall'esterno, per esempio.

Quello che non puoi fare (come hai notato) è usare le variabili per memorizzare le espressioni XPath.

+0

Domanda stupida: perché è possibile farlo con i parametri, ma non con le variabili? – Colen

+0

@Colen: la variabile (o param, per quella questione) '$ filter' memorizza una stringa, non un'espressione.Ho scelto' | 'come delimitatore ;-) – Tomalak

1

Sia XSLT 1.0 e XSLT 2.0 non supportano la valutazione dinamica.

Un modo per farlo è usare <xsl:function> in XSLT 2.0 o <xsl:call-template> in XSLT 1.0.

<xsl:function name="my:test" as="xs:boolean"> 
    <xsl:param name="pNode" as="element()"/> 

    <xsl:variable name="vid" select="$pNode/@id"/> 

    <xsl:sequence select= 
    "$vid and not($vid=('_Name_','Group','_Count_')"/> 
</xsl:function> 

allora si potrebbe utilizzare questa funzione:

<xsl:apply-templates select="columnval[my:test(.)]"/> 

Certo, è possibile specificare il test in specifici tracciati partita come suggerito da Robert Rossney, e questo potrebbe essere il modo migliore.

In caso di necessità di dinamicamente definire quale funzione di filtro da usare, uno strumento potente è la libreria FXSL, che implementa-Order-funzioni superiori (HOF) in XSLT. HOF sono funzioni che accettano altre funzioni come parametri e possono restituire una funzione come risultato.

Utilizzando questo approccio, determinare dinamicamente e passare a my:test() come parametro una funzione che esegue il test.

2

Refactoring @ Robert Rossney e @Tomalak

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-templates select="someothernode" mode="filter"/> 

<xsl:template match="*" mode="filter"> 
    <xsl:param name="pFilter" select="'_Name_|Group|_Count_'"/> 
    <xsl:apply-templates select="self::* 
           [not(contains( 
             concat('|',$pFilter,'|'), 
             concat('|',@id,'|'))) 
           and @id]"/> 
</xsl:template> 
+1

Poiché gli attributi sono per definizione membri di elementi, è possibile sostituire il generico 'node()' da '' *. :-) – Tomalak