I've been learning to use the mode attribute with XSLT and was wondering if there's a way to test for it within a template, such as in an xsl:if statement? I've only seen it used at the xsl:template level and maybe that's the only way. Say I want to add a "../" in front of a path attribute (#href), but only if mode="print":
<xsl:template name="object" mode="#all">
<img>
<xsl:attribute name="src">
<xsl:if test="mode='print'"><xsl:text>../</xsl:text></xsl:if>
<xsl:value-of select="#href"/>
</xsl:attribute>
</img>
</xsl:template>
I'm calling apply-templates with and without the mode="print" set from various other templates.
Of course I can make a new template with mode="print" but then I'd have to maintain two templates.
Or maybe there's a better way to do this? Thanks for the help. - Scott
There is no direct way to do it yet. One approach can be-
<xsl:template match="/">
<xsl:apply-templates select="something" mode="a">
<xsl:with-param name="mode" select="'a'" tunnel="yes"/>
</xsl:apply-templates>
<xsl:apply-templates select="something" mode="b">
<xsl:with-param name="mode" select="'b'" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
and then in the match-
<xsl:template match="blah" mode="a b">
<xsl:param name="mode" tunnel="yes"/>
<xsl:if test="$mode='a'">
<!-- Do Something -->
</xsl:if>
<xsl:if test="$mode='b'">
<!-- Do Something -->
</xsl:if>
</xsl:template>
There's no way to get the current mode, but you can do this:
<xsl:template match="object" mode="#all">
<xsl:param name="print" select="false()"/>
<!-- Your code here -->
</xsl:template>
<xsl:template match="object" mode="print">
<xsl:next-match>
<xsl:with-param name="print" select="true()"/>
</xsl:next-match>
</xsl:template>
Related
Is it possible to forward all param received in a template to another without knowing them ?
Example :
<xsl:template match="foo">
<xsl:apply-templates select="bar">
<xsl:with-param name="father-id" select="#id"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*" priority="9">
<!-- do some things -->
<xsl:next-match/>
</xsl:template>
<xsl:template match="bar">
<xsl:param name="father-id"/>
<!-- do some things with my param -->
</xsl:template>
Here my param father-id is lost because of my xsl:template match="*".
So, is there a way to forward it at the <xsl:next-match /> step but not using the following code because there can be more cases than this one and with different params ?
<xsl:template match="*" priority="9">
<xsl:param name="father-id"/>
<!-- do some things -->
<xsl:next-match>
<xsl:with-param name="father-id" select="{father-id}"/>
</xsl:next-match>
</xsl:template>
Thank you in advance.
Gerald.
Try it this way:
<xsl:template match="foo">
<xsl:apply-templates select="bar">
<xsl:with-param name="father-id" select="#id" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*" priority="9">
<!-- do some things -->
<xsl:next-match/>
</xsl:template>
<xsl:template match="bar">
<xsl:param name="father-id" tunnel="yes"/>
<!-- do some things with my param -->
</xsl:template>
XSLT 2.0 only - but then so is xsl:next-match.
http://www.w3.org/TR/xslt20/#tunnel-params
What would be an efficient way to reorder a group of nodes selected using xsl:choose (XSLT 1.0).
Below is the sample source XML:
<Universe>
<CObj>
<Galaxies>
<Galaxy>
<Profile>
<Name>MilkyWay</Name>
<Age>12.5</Age>
</Profile>
<PlanetarySystem>
<Name>Solar</Name>
<Location></Location>
<Planet>
<Name>Earth</Name>
<Satellite>Y</Satellite>
...
...
...
</Planet>
...
...
...
</PlanetarySystem>
<PlanetarySystem>
...
...
...
</PlanetarySystem>
</Galaxy>
<Galaxy>
...
...
...
</Galaxy>
</Galaxies>
</CObj>
</Universe>
XSL snippet:
<xsl:template name="get-galaxy-types">
<xsl:variable name="galaxy_age1" select ="1" />
<xsl:variable name="galaxy_age2" select ="5" />
<xsl:variable name="galaxy_age3" select ="10" />
<xsl:for-each select="Galaxies/Galaxy/Profile/Age">
<xsl:choose>
<xsl:when test=".=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test=".=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType3>
</xsl:when>
<xsl:when test=".=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType1>
</xsl:when>
</xsl:choose>
</xsl:for-each>
Above XSL template is called from main template like:
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:call-template name="get-galaxy-types"/>
</GalaxyTypes>
</xsl:template>
Output XML:
Note that the order of <GalaxyType> cannot be changed.
<Universe>
...
...
...
<GalaxyTypes>
<GalaxyType2>xxxxxx</GalaxyType2>
<GalaxyType3>xxxxxx</GalaxyType3>
<GalaxyType1>xxxxxx</GalaxyType1>
</GalaxyTypes>
...
...
...
</Universe>
Since xsl:choose returns the XML nodes as and when it finds a match I am unable to find a straight forward way to control the order in which I want GalaxyType to appear in the output XML.
How can I have a generic template to perform reordering for any elements that might get added in the future that may fall in to similar requirement. I am fine with having a remapping template within this XSL but I am not really sure how to accomplish this in a really elegant and efficient way.
I am going to guess you want to put the galaxies matching galaxy-age1 first, then the ones matching galaxy-age2 next, and finally the ones for galaxy-age3. I am also assuming the ages specified may not be in ascending order (that is to say, galaxy-age3 could be less that galaxy-age1.
To start with, it might be more natural to do your xsl:for-each over the Galaxy elements
<xsl:for-each select="Galaxies/Galaxy">
Then, to do your customisable sort, you could first define a variable like so...
<xsl:variable name="sortAges"
select="concat('-', $galaxy_age1, '-', $galaxy_age2, '-', $galaxy_age3, '-')" />
Note the order the parameters appear in the concat statement corresponds to the order they need to be output.
Then, your xsl:for-each could look this this...
<xsl:for-each select="Galaxies/Galaxy">
<xsl:sort select="string-length(substring-before($sortAges, concat('-', Profile/Age, '-')))" />
But this is not very elegant. It might be better to simply have a template that matches Galaxy and use three separate xsl:apply-templates to select the Galaxy; one for each age.
Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="5" />
<xsl:param name="galaxy_age3" select="10" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age1]">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age2]">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age3]">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
EDIT: To make this more efficient, consider using a key to look up the Galaxy elements by their name. Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="10" />
<xsl:param name="galaxy_age3" select="5" />
<xsl:key name="galaxy" match="Galaxy" use="Profile/Age" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="key('galaxy', $galaxy_age1)">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age2)">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age3)">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It's very difficult to navigate between the scattered snippets of your code. Still, it seems to me you should change your strategy to something like:
<xsl:template match="?">
...
<GalaxyTypes>
<xsl:apply-templates select="??/???/Galaxy">
<xsl:sort select="Profile/Age" data-type="number" order="ascending"/>
</xsl:apply-templates=>
</GalaxyTypes>
...
</xsl:template>
<xsl:template match="Galaxy">
<xsl:choose>
<xsl:when test="Profile/Age=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="Profile/Name"/>
</GalaxyType1>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="Profile/Name"/>
</GalaxyType3>
</xsl:when>
</xsl:choose>
</xsl:template>
--
Note that your output would be much better formatted if all galaxies were a uniform <Galaxy> element, with a type attribute to tell them apart.
I'm trying to write a recursive named template that will show the path of a given node:
<?xml version="1.0"?>
<testfile>
<section>
<title>My Section</title>
<para>Trying to write a recursive function that will return a basic xpath of a given node; in the case of this node, I would want to return testfile/section/para, I don't need /testfile/section[1]/para[1] or anything like that. The issue I'm having is that in the case of a named template, I don't know how to select a different node and apply it to the named template.</para>
</section>
</testfile>
I'm trying this template :
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet to test a named template trying to build an xpath for a given node -->
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:call-template>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="ancestor::*">
<xsl:message><xsl:value-of select="name()"/> has a parent</xsl:message>
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name()"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
<!-- how to recursively call template with parent node? -->
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name()"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As per the comment, I'm not sure how to apply a node other than the context node to the named template. The other strategy I tried was to send the node to the template as a param, but I don't know how(or if you can) apply an axis to a param, as in
$thisNode../*
etc.
I'm sure it's something simple that I'm missing...thanks.
You can indeed pass in the node as a param to the template....
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:param name="node" select="." />
To apply an axis to it, for example to test for ancestors, you would do this....
<xsl:when test="$node/ancestor::*">
And to pass its parent element to the template when you recursively call it, do this:
<xsl:with-param name="node" select="$node/parent::*" />
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:call-template>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:param name="node" select="." />
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="$node/ancestor::*">
<xsl:message><xsl:value-of select="name($node)"/> has a parent</xsl:message>
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name($node)"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
</xsl:with-param>
<xsl:with-param name="node" select="$node/parent::*" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name($node)"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
An alternate approach is to use xsl:apply-templates, but with the mode parameter to keep it separate from other template matches. Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:apply-templates select="." mode="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:apply-templates>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*" mode="getXpath">
<xsl:param name="pathText"/>
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="ancestor::*">
<xsl:message><xsl:value-of select="name()"/> has a parent</xsl:message>
<xsl:apply-templates select=".." mode="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name()"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name()"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You shouldn't have to pass a node as a param if you just do an xsl:for-each.
Here's a modified example of your XSLT. (Notice that the positional predicates are only output in the path if they are needed.)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet to test a named template trying to build an xpath for a given node -->
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath"/>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/',local-name())"/>
<!--Predicate is only output when needed.-->
<xsl:if test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Output (using the input from the question)
<result>
<element>element name : testfile path : /testfile</element>
<element>element name : section path : /testfile/section</element>
<element>element name : title path : /testfile/section/title</element>
<element>element name : para path : /testfile/section/para</element>
</result>
For what it's worth, I wrote a simple XPath generation template about a decade ago, in part 2 of my "styling stylesheets" article on DeveloperWorks:
Listing 4. Template that generates a Pseudo XPath in XSLT
<xsl:template name="pseudo-xpath-to-current-node">
<!-- Special-case for the root node, which otherwise
wouldn't generate any path at all. A bit of a kluge,
but it's simple and efficient. -->
<xsl:if test="not(parent::node())">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:for-each select="ancestor-or-self::node()">
<xsl:choose>
<xsl:when test="not(parent::node())">
<!-- This clause recognizes the root node, which doesn't need
to be explicitly represented in the XPath. -->
</xsl:when>
<xsl:when test="self::text()">
<xsl:text>/text()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::comment()">
<xsl:text>/comment()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<xsl:text>/processing-instruction()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::*">
<!-- This test for Elements works because the Principal
Node Type of the self:: axis happens to be Element.
-->
<xsl:text>/</xsl:text>
<xsl:value-of select="name(.)"/>
<xsl:text>[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::node()[name()='xmlns' | starts-with(name(),'xmlns:')]">
<!-- This recognizes namespace nodes, though it's a bit
ugly. XSLT 1.0 doesn't seem to have a more elegant
test. XSLT 2.0 is expected to deprecate the whole
concept of namespace nodes, so it may become a moot
point.
NS nodes are unique; a count isn't required. -->
<xsl:text>/namespace::</xsl:text>
<xsl:value-of select="local-name(.)"/>
</xsl:when>
<xsl:otherwise>
<!-- If I've reached this clause, the node must be an
attribute. Attributes are unique; a count is not
required. -->
<xsl:text>/#</xsl:text>
<xsl:value-of select="name(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
That was an XSLT 1.0 solution, structured for clarity. It's probably possible to simplify it, especially if you're using XSLT and XPath 2.0.
As I explained there, this "pseudo-XPath" version ignores the namespace issue, since I didn't need it for that proof-of-concept tool and since it was intended for human-readable messages rather than for execution. It could be corrected to manage namespaces properly by changing it to write out paths that specify node type with a predicate explicitly testing localname and namespace URI. The resulting paths would be bulkier and harder for humans to process. Exercise for the reader, if you're so inclined.
You might also be able to replace the positional index with something more expressive... but knowing what's going to be meaningful is not easy.
Hope that helps. Have fun.
(Oh, almost forgot: I wouldn't be surprised if there are other solutions on the XSLT FAQ site.)
I think you want something like this:
<xsl:variable name="get.path">
<xsl:text> /</xsl:text>
<xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="get.current.node" select="name(.)"/>
<xsl:value-of select="name()"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name(.) = $get.current.node]) + 1"/>
<xsl:text>]</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
Hello i need to achieve the below functionality in my XSL but it seems i'm stuck... Any help will be much appreciated.
Please see my comments inside the below code snippet.
<xsl:template name="/">
<xsl:call-template name="looptemplate">
<xsl:with-param name="x" select="1"/>
<xsl:with-param name="max" select="10"/>
</xsl:call-template>
</xsl:template>
<xsl:template name=" looptemplate">
<xsl:param name="x"/>
<xsl:param name="max"/>
<xsl:call-template name="TemplateToCall">
<xsl:with-param name="nodePath" select="a/b$i"></xsl:with-param>
<!--
Get dynamically root nodes
a/b1, a/b2, a/b3 etc
-->
</xsl:call-template>
<!--
Loop again until x reaches max
-->
</xsl:template>
<xsl:template name="TemplateToCall">
<xsl:param name="nodePath"/>
<xsl:for-each select="$nodePath">
<xsl:value-of select="value1"/>, <xsl:value-of select="value2"/>
</xsl:for-each>
</xsl:template>
You can't build an XPath as a string and evaluate it dynamically like that (at least not in plain XSLT 1.0 or 2.0, there will be an xsl:evaluate instruction in XSLT 3.0), but you could do something like
<xsl:call-template name="TemplateToCall">
<xsl:with-param name="nodes" select="a/*[local-name() = concat('b', $i)]"/>
and then in the called template
<xsl:template name="TemplateToCall">
<xsl:param name="nodes"/>
<xsl:for-each select="$nodes">
I have a piece of code that look similar to this:
<xsl:choose>
<xsl:when test="some_test">
<xsl:value-of select="Something" />
You are:
<xsl:variable name="age">12</xsl:variable>
years
</xsl:when>
</xsl:choose>
My problem is that I would like to use the variable $age outside of the choose. How do I do that?
A similar problem is that I have several templates in my XSLT-file, and one of them is the main template. This one:
<xsl:template match="/">
</xsl:template>
Inside of this template, I call several other templates, and again I would like to use some of the variables from the other templates.
In example, if I have this code:
<xsl:template match="/">
<xsl:call-template name="search">
</xsl:call-template>
</xsl:template>
<xsl:template name="search">
<xsl:variable name="searchVar">Something...</xsl:variable>
</xsl:template>
Then I would like to use the $searchVar inside of my main-template.
It's kind of the same issue I think, but I can't seem to figure this one out.
And I run Umbraco as my CMS by the way :)
I hope that some of you have the answer.
Thanks
- Kim
To #1: Variables are valid within their parent element only. Which means you must put the logic inside the variable instead of "around" it:
<xsl:variable name="var">
<xsl:choose>
<xsl:when test="some_test">
<xsl:text>HEY!</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>SEE YA!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
To #2: Use parameters to transport values into templates.
<xsl:template match="/">
<xsl:variable name="var" select="'something'" />
<!-- params work for named templates.. -->
<xsl:call-template name="search">
<xsl:with-param name="p" select="$var" />
</xsl:call-template>
<!-- ...and for normal templates as well -->
<xsl:apply-templates select="xpath/to/nodes">
<xsl:with-param name="p" select="$var" />
</xsl:apply-templates>
</xsl:template>
<!-- named template -->
<xsl:template name="search">
<xsl:param name="p" />
<!-- does stuff with $p -->
</xsl:template>
<-- normal template -->
<xsl:template match="nodes">
<xsl:param name="p" />
<!-- does stuff with $p -->
</xsl:template>
To transport a value back to the calling template, combine the above:
<xsl:template match="/">
<xsl:variable name="var">
<xsl:call-template name="age">
<xsl:with-param name="num" select="28" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$var" />
</xsl:template>
<xsl:template name="age">
<xsl:param name="num" />
<xsl:value-of select="concat('You are ', $num, ' years yold!')" />
</xsl:template>
Have a look at xsl:param.
Edit: Untested, but may work:
<xsl:param name="var">
<xsl:choose>
<xsl:when test="some_test">
<xsl:value-of select="string('HEY!')" />
</xsl:when>
</xsl:choose>
</xsl:param>