xslt
2010-10-07 10 views 9 likes 
9

Mam dość skomplikowany wywołanie xsl: apply-templates:Jak używać zmiennej XSL w xsl: apply-templates?

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

Wyrażenie jest ponownie w innych miejscach tak:

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

chcę generalizować go jakoś, więc mogę określić raz i ponownie użyć go gdzie indziej. Jednak to nie działa:

<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]"/> 

Czy istnieje lepszy/inny sposób robienia tego? Wszystko, czego chcę, to ponowne użycie wyrażenia xpath w wielu różnych wywołaniach do xsl: apply-templates (niektóre z nich wybiera się z różnych dzieci).

To będzie używane w aplikacji klienckiej, więc nie mogę używać żadnych rozszerzeń lub przełączam się na XSLT 2 niestety. :(

Dzięki.

+0

Dobre pytanie. Zobacz moją odpowiedź na opis dwóch możliwych rozwiązań (XSLT 1.0 i XSLT 2.0) oraz podpowiedź bardziej wydajnego rozwiązania wykorzystującego funkcje wyższego rzędu. –

Odpowiedz

5

Nie można zbudować XPath dynamicznie w XSLT (przynajmniej nie XSLT 1.0). Ale można łatwo osiągnąć to, co starasz się zrobić stosując tryby Szablon:

<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 To jest efektywne podejście Jedynym minusem jest to, że filtr jest zakodowany i dlatego nie jest zmienny.Jeśli filtr zmienny nie jest potrzebny, chciałbym pójść z tym rozwiązaniem – Tomalak

+0

Tryby szablonów nie miały dla mnie sensu w ogóle, dopóki nie zacząłem borykać się z problemami takimi jak OP. –

1

chciałbym przyjrzeć się używając rozszerzenia do XSLT. Nie sądzę, że można to zrobić w „standardowym” XSLT.

Rozszerzenie to może robić, co chcesz : http://www.exslt.org/dyn/functions/evaluate/index.html

+0

Zaktualizowane pytanie - nie możemy używać rozszerzeń, ponieważ polegamy na MSXML, aby wykonać transformację :( – Colen

+0

'msxsl: node-set' zadziała, następnie –

1

z exsl rozszerzenia: zbiorze węzłów, można utworzyć nazwie szablonu, który akceptuje zbiorze węzłów $ x i zwraca filtrowane zbiorze węzłów w zależności od statycznego orzecznika

można również zdefiniować funkcję, w XSLT 2.0.

+0

Zaktualizowane pytanie - niestety utknęliśmy przy XSLT 1.0.: ( – Colen

1

Jak o:

<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> 

Można zrobić $filter param, i przekazać je z zewnątrz, na przykład.

Czego nie możesz zrobić (jak zauważyłeś) to zmienne używane do przechowywania wyrażeń XPath.

+0

Głupie pytanie - dlaczego możesz to zrobić z parametrami, ale nie zmiennymi? – Colen

+0

@ Colen: Zmienna (lub parametr, o to chodzi) '$ filter' przechowuje ciąg znaków, a nie wyrażenie. delimiter; ;-) – Tomalak

1

Zarówno XSLT 1.0 i XSLT 2.0 nie obsługują dynamiczną ocenę.

Jednym ze sposobów, aby to zrobić jest użycie <xsl:function> w XSLT 2.0 lub <xsl:call-template> w 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> 

następnie można użyć tej funkcji:

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

Oczywiście, można określić test w określonych wzorców wynik jak sugeruje Robert Rossney, a to może być najlepszym sposobem.

W przypadku trzeba dynamicznie określić, która funkcja filtrowania użyć, jednym potężnym narzędziem jest biblioteka FXSL, który realizuje wyższego rzędu-funkcji (Hof) w XSLT. HOF to funkcje, które przyjmują inne funkcje jako parametry i mogą zwracać funkcję jako wynik.

Stosując to podejście, dynamicznie określasz i przekazujesz do parametru my:test() jako parametr funkcję, która wykonuje test.

2

Refaktoryzacja @Robert Rossney i @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

Ponieważ atrybuty są z definicji elementami elementów, można zamienić ogólny "węzeł()" na '*'. :-) – Tomalak

Powiązane problemy