How to call an xslt template which has no name? - xslt

My xslt template looks like this:
<xsl:template match="text()">
<xsl:param name="precedingPStyle" select="preceding-sibling::aic:pstyle[position()=1]/#name"/>
</xsl:template>
Is above a valid xslt template? How/when can this template be called? it has no name, only a match and the match has a parameter.

It will be called by xsl:apply-templates when it is the most appropriate template for the node selected. In the absence of any other more specific templates such as match="text()[normalize-space(.)]" this template would be applied for all text nodes.
For parameters, apply-templates supports with-param in exactly the same way as call-template does.
<xsl:apply-templates select="*/text()">
<xsl:with-param name="precedingPStyle" select="'normal'"/>
</xsl:apply-templates>
The with-param select expression is evaluated in the context of the call, not the target node to which the template applies. As with call-template, any parameters that are not set with an explicit with-param will take the default value specified by the select expression on the xsl:param element in the template (which is evaluated in the context of the target, not the call)

Related

Is there a better way than xsl:variable to refer to an attribute value inside an XPath expression?

I am using XSLT 2.0 to transform some XML. The source XML looks similar to this:
<AnimalTest>
<AnimalTypes>
<AnimalType name="cat"/>
<AnimalType name="dog"/>
</AnimalTypes>
<Animals>
<Animal name="Sylvester" typeName="cat"/>
<Animal name="Fido" typeName="dog"/>
<Animal name="Tom" typeName="cat"/>
</Animals>
</AnimalTest>
Inside the XSL template to handle AnimalType tags, I want to use the name attribute of the AnimalType inside an XPath expression. The only way I have been able to achieve this, is by introducing a variable that holds the attribute #name and is referred from inside the XPath expression, like this:
<xsl:template match="AnimalType">
<xsl:variable name="typename" select="#name"/>
<xsl:apply-templates select="/AnimalTest/Animals/Animal[#typeName=$typename]"/>
</xsl:template>
This works, but I wonder whether I really have to use this temporary variable. Is there any better way to refer to that #name attribute? It looks like a detour to me.
If you really disliked using the variable, you could use the current() function to refer to the current context node (AnimalType in your case)
<xsl:apply-templates select="/AnimalTest/Animals/Animal[#typeName=current()/#name]"/>
If you had a more complex expression, using a variable can improve readability though, and you could potentially re-use in other places.
One thing to note is that his declaration
<xsl:variable name="typename" select="#name"/>
Is not quite the same as this declaration
<xsl:variable name="typename">
<xsl:value-of select="#name" />
</xsl:variable>
Although both variables will contain the same value. In the latter case (using xsl:value-of) you are creating a copy of the value of the name attribute. In the former case, you are referring to the attribute directly. Therefore using the latter format would be less efficient.
As a slight aside, you may consider using a key here to look up your Animal elements by their typeName
<xsl:key name="AnimalByType" match="Animal" use="#typeName" />
That way, your apply-templates expression can be simplified to just the following
<xsl:template match="AnimalType">
<xsl:apply-templates select="key('AnimalByType', #name)"/>
</xsl:template>

How to use an XSL variable as a condition to evaluate XPath?

I have faced an issue when using a variable as a condition for XPath evaluation. I have the following template which works fine:
<xsl:template name="typeReasonDic">
<xsl:variable name="dic" select="$schema//xs:simpleType[#name = 'type_reason_et']"/>
<!-- do something with the variable -->
</xsl:template>
However, when I change it to look like this:
<xsl:template name="typeReasonDic">
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
<xsl:variable name="dic" select="$schema//xs:simpleType[$choose_dic]"/>
<!-- do something with the variable -->
</xsl:template>
it fails to find the desired node.
What I wish to get is a template with a default value for $choose_dic which can be overriden where necessary.
What am I missing here?
UPD: there is this link I found with the description of what I'm trying to do, but it doesn't seem to work for me.
You can't do this directly in XSLT 1.0 or 2.0 without an extension function. The problem is that with
<xsl:template name="typeReasonDic">
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
<xsl:variable name="dic" select="$schema//xs:simpleType[$choose_dic]"/>
<!-- do something with the variable -->
</xsl:template>
the <xsl:param> will evaluate its select expression a single time in the current context and store the true/false result of this evaluation in the $choose_dic variable. The <xsl:variable> will therefore select either all xs:simpleType elements under the $schema (if $choose_dic is true) or none of them (if $choose_dic) is false. This is very different from
<xsl:variable name="dic" select="$schema//xs:simpleType[#name = 'type_reason_et']"/>
which will evaluate #name = 'type_reason_et' repeatedly, in the context of each xsl:simpleType, and select those elements for which the expression evaluated to true.
If you store the XPath expression as a string you can use an extension function such as dyn:evaluate or the XSLT 3.0 xsl:evaluate element if you're using Saxon.
By doing
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
the XSL engine will try to evaluate "#name = 'type_reason_et'" as an XPath expression, and will assign the RESULT to your variable.
You should use the following variable declaration instead:
<xsl:param name="choose_dic">#name = 'type_reason_et'</xsl:param>
This is the default value, but you can override it when you call your template by using xsl:with-param.
XSLT is not a macro language where you might be able to concatenate your code at run-time from strings and then evaluate them dynamically. So in general for your purpose you would need an extension function to evaluate an XPath expression stored in a string or you need to look into a new XSLT 3.0 features like http://www.saxonica.com/documentation/xsl-elements/evaluate.xml.
What is possible in the scope of XSLT 1.0 or 2.0 is doing e.g.
<xsl:param name="p1" select="'foo'"/>
<xsl:variable name="v1" select="//bar[#att = $p1]"/>
where the param holds a value you compare to other value, for instance those in a node like an attribute or element node.

XSL beginner question

I want to understand, in general, what this means:
<xsl:template match="foo:barLists[#mode = 'Dummy Filter']"
mode="dummy-filter-cache" priority="2">
I'm looking for some insight to what this does so I may learn a bit about XSL
<xsl:template
This element defines a template. We'll give it data later with an apply-template element
match="foo:barLists[#mode = 'Dummy Filter']"
This template uses the element barLists in the namespace foo which has an attribute of mode which is set to "Dummy Filter". i.e. <foo:barList mode="Dummy Filter"> .... </foo:barList>
mode="dummy-filter-cache"
This tempalte has a mode of "dummy-filter-cache". I have no idea what that means. w3schools.com only says about mode: "Optional. Specifies a mode for this template"
priority="2">
This tempate has a priority of 2. If there's another template which also matches that element with a priority of 1, that one wins.
In general, you are matching specific nodes with specific attributes in an XML file.
I suggest you look at a tutorial on XSL.
mode attribute of xsl:template allows you to create several templates that have same match attribute. With mode you can choose which one of these templates gets applied in different cases. This might be useful if you need to apply the same content several times with different formatting at some of these times.
A template with a mode will be instantiated only when you have set the same mode on an xsl:apply-templates element whose select attribute matches the match attribute on the xsl:template element.
Let's suppose you have templates
<xsl:template match="foo">
and
<xsl:template match="foo" mode="bar">
Then <xsl:apply-templates select="foo" mode="bar"/> will match the template #2 while
<xsl:apply-templates select="foo"/> and <xsl:apply-templates/> will match the template #1.

Select xsl element based on index that is defined in a param

I want to select an element by index with the indexed number being passed in with a param, the param is being passed in via PHP. Here's what I am trying:
//PHP
$xslt->setParameter('','player',$player);
$xslt->importStylesheet( $XSL );
print $xslt->transformToXML( $data );
//xslt
<xsl:param name="player" data-type="number"/>
<template match="/">
<xsl:value-of select="result[$player]/#name" />
</template>
And I know the value of the param is being passed correctly because I can just output the value of the param ($player) and it will output the correct value. If I hard code the indexed number "$player" to any number of index I want like below:
<template match="/">
<xsl:value-of select="result[2]/#name" />
</template>
it works. So, what I am doing wrong here. Can you not use params/variables to select indexes?
It may be evaluating the value of your xsl:param as a string, rather than a number. You can try explicitly converting it to a number using the number() function.
<xsl:value-of select="result[number($player)]/#name" />
The predicate filter specifying a number is short-hand for [position()=$param]. You can use xsl:param inside the predicate filter, like this, and it will evaluate the xsl:param value as a number:
<xsl:value-of select="result[position()=$player]/#name" />
If I hard code the indexed number
"$player" to any number of index I
want like below:
<template match="/">
<xsl:value-of select="result[2]/#name" />
</template>
it works.
No, any compliant XSLT processor will not select anything.
result[2]/#name
is a relative expression against the current node, and the current node is the / -- document-node.
Any well-formed XML document has exactly one top element (never two), therefore
result[2]
is equivalent to:
/result[2]
and doesn't select anything.
Most probably you are dealing with another expression, which you haven't shown (or the template is not matching just /).
Also:
<xsl:param name="player" data-type="number"/>
this is invalid syntax. The <xsl:param> instruction doesn't have a data-type attribute.
In fact, in XSLT 1.0 there isn't any way to specify the type of variables or parameters.
This is why in:
result[$player]/#name
$player is treated as string -- not as an integer.
To achieve the "indexing" you want, use:
result[position()=$player]/#name
The position() function returns a number and this causes the other operand of the = operator to be converted to (and used as) number.

Select template for execution using a condition including variable on apply-templates

I have a template that looks like below
<xsl:template match="more-info" mode="docuSection">
html
</xsl:template>
and which is applied with the call
<xsl:apply-templates select="." mode="docuSection"/>
so the template is applied when the current node has more-info element, is there a way to make this template get applied with the same call and with the condition which includes a global variable e.g. match="$mode='edit' or more-info"
Best Regards,
Keshav
is there a way to make this template
get applied with the same call and
with the condition which includes a
global variable e.g.
match="$mode='edit' or more-info"
In XSLT 2.0 this is perfectly legal:
<xsl:template match="more-info[$mode = ('edit', 'more-info')]"
mode="docuSection">
In XSLT 1.0 it is forbidden to use variable or key references within a match pattern.
However, one can use either of the following techniques:
I. Within the <xsl:apply-templates> instruction specify the exact node-list of nodes to be processed.
<xsl:apply-templates mode="docuSection"
select="self::*[$mode = 'edit' or $mode='more-info']" />
||. Make the match pattern more general, but do any processing within the template only if the desired condition is fulfilled:
<xsl:template match="more-info" mode="docuSection">
<xsl:if test="$mode = 'edit' or $mode='more-info'">
html
</xsl:if>
</xsl:template>