Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying different XSLT template depending on a parameter

Tags:

asp.net

xslt

What I have?

I am have an ASP.NET project in which, I have an XSLT file with many templates defined. Only one template will be used at a time depending on the user selection to display the content on a web page. It looks something like this:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:param name="TemplateName"></xsl:param>

      <xsl:template match="Title_Only">
          ...template Title_Only body...
      </xsl:template>

      <xsl:template match="Image_Only">
          ...template Image_Only body...
      </xsl:template>

      <xsl:template match="Title_And_Image">
          ...template Title_And_Image body...
      </xsl:template>
    </xsl:stylesheet>

What I want?

I want to pass the template name TemplateName as a parameter and to be able apply the respective template on the data.

Can someone please tell me how can I achieve this?

like image 636
Vijay Avatar asked Apr 07 '11 20:04

Vijay


2 Answers

You cannot use the value of a param or variable in a match pattern in XSLT 1.0. However, you could apply templates conditionally from a single template, like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="TemplateName"/>
    <xsl:template match="/">
        <xsl:apply templates select="something[some_condition=$TemplateName]"/>
    </xsl:template>
</xsl:stylesheet>

...and then just set-up templates to match each type of node individually. Templates will be applied only to those things that match your select expression, which is based on the param.

Example of conditionally applying templates

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="TemplateName" select="'Title_Only'" />
    <xsl:template match="/">
        <xsl:apply-templates select="test/val[@name=$TemplateName]" />
    </xsl:template>
    <xsl:template match="val">
        <xsl:value-of select="@name" />
    </xsl:template>
</xsl:stylesheet>

Applied to this input:

<test>
    <val name="Title_Only" />
    <val name="Image_Only" />
    <val name="Title_And_Image" />
</test>

Produces:

Title_Only

...based on the value of $TemplateName. (Note that this example uses a variable, but the idea is the same.)

Separate templates using modes

If you really need an entirely different template in each case, then use modes. This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="TemplateName" select="'Title_Only'" />
    <xsl:template match="/">
        <xsl:choose>
            <xsl:when test="$TemplateName='Title_Only'">
                <xsl:apply-templates select="test/val" mode="Title_Only" />
            </xsl:when>
            <xsl:when test="$TemplateName='Image_Only'">
                <xsl:apply-templates select="test/val" mode="Image_Only" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="test/val" mode="Title_And_Image" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="val" mode="Title_Only">
        <xsl:value-of select="@title" />
    </xsl:template>
    <xsl:template match="val" mode="Image_Only">
        <xsl:value-of select="@img" />
    </xsl:template>
    <xsl:template match="val" mode="Title_And_Image">
        <xsl:value-of select="@title" />/
        <xsl:value-of select="@img" />
    </xsl:template>
</xsl:stylesheet>

Applied to this source:

<test>
    <val title="some title" img="some image"/>
</test>

Produces:

some title

The desired template is used, based on the value of the param.

like image 130
Wayne Avatar answered Nov 15 '22 11:11

Wayne


I want to pass the template name TemplateName as a parameter and to be able apply the respective template on the data.

In both XSLT 1.0 and XSLT 2.0 it is illegal to have a construct like:

<xsl:call-template name="{$vTemplateName}"/>

While XPath 3.0 (XSLT 3.0) introduces function items and HOF (higher-order functions), HOF can be emulated in previous verions of XSLT. For more information, do read the articles on the home page of FXSL.

Here is a simplified example of the idea lying behind FXSL:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

Given this sample XML, we have two templates, one which produces the sum of all num elements and the other produces their concatenation. We want to pass the desired operation as a parameter.

Here is how to do this (note that nothing in the source XML itself tells us which operation to use):

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

 <xsl:param name="pOp" select="'sum'"/>

 <my:ops>
  <op>sum</op>
  <op>concat</op>
 </my:ops>

 <xsl:variable name="vOps" select=
   "document('')/*/my:ops/*"/>

 <xsl:template match="/">
  <xsl:apply-templates select="$vOps[. = $pOp]">
   <xsl:with-param name="arg1" select="/*/*"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="op[.='sum']">
  <xsl:param name="arg1"/>
  <xsl:value-of select="sum($arg1)"/>
 </xsl:template>

 <xsl:template match="op[.='concat']">
  <xsl:param name="arg1"/>

  <xsl:for-each select="$arg1">
   <xsl:value-of select="."/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When applied on the XML document above, the wanted, correct result is produced:

55

when we replace:

 <xsl:param name="pOp" select="'sum'"/>

with:

 <xsl:param name="pOp" select="'concat'"/>

and apply the new transformation, again the wanted, correct result is produced:

01020304050607080910

Do note:

The main (using) template can and typically will be in a separate XSLT stylesheet file and will import stylesheets with templates that implement operations. The main template doesn't know what are the operations implemented (and doesn't use an <xsl:choose> with hardcoded names).

In fact, if templates are added or removed from the imported files, there is no need to modify the main (using) template. In this style of programming the <xsl:apply-templates> instruction often selects for execution templates which were not yet written, when the main template was produced.

like image 20
Dimitre Novatchev Avatar answered Nov 15 '22 11:11

Dimitre Novatchev