XSLT - Preserving disable-output-escaping in a copy-of - xslt

I seem to be having an issue preserving the disable-output-escaping when using that value inside of an xsl:copy-of.
Here's my code:
<xsl:call-template name="Display">
<xsl:with-param name="text">
<xsl:value-of select="content" disable-output-escaping="yes" />
</xsl:with-param>
</xsl:call-template>
<xsl:template name="Display">
<xsl:param name="text" />
<span><xsl:copy-of select="$text" /></span>
</xsl:template>
Any special characters that were kept as-is from the xsl:value-of statement are escaped when they're used in the xsl:copy-of statement.
For example:
<xsl:value-of select="$text" disable-output-escaping="yes"> will display this: รจ
<xsl:copy-of select="$text"> will display &#232
I'd like to know if there is any way around this?

As per Spec, the disable-output-escaping attribute can be specified only on <xsl:value-of> and the <xsl:text> instructions.
You need the DOE only on the xslt instruction that actually outputs the value, not on one that sets a parameter value.
Solution:
Replace:
<span><xsl:copy-of select="$text"/></span>
with:
<span><xsl:value-of select="$text" disable-output-escaping="yes"/></span>
Do note: Typically one should avoid using DOE, as it breaks the XSLT architectural model and usually isn't needed. Also, the DOE feature isn't mandatory and not all XSLT 1.0 processors support it.
Note 2: You don't actually need DOE in your case at all. The output from the XSLT transformation should be displayed by the browser as expected.

disable-output-escaping controls the action of the serializer when handed a text node. It's meaningless when the text node isn't being handed to a serializer, for example when it is added to a temporary tree.

Related

Can I not access my xsl:variable this way when I'm outputting plain text

I'm doing an xslt transform that generates a sql statement for me. The way I'm using below is not working. Is there a way?
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select $var1.* from $var1
</xsl:template>
I know it will work if I do this:
<xsl:template match="foo">
select <xsl:value-of select="#att_val1" />.* from <xsl:value-of select="#att_val1" />
</xsl:template>
In XSLT 1.0, variable references are recognized in XPath expressions, but not in general template text. To evaluate an XPath expression and output the result as a text node in the result tree, use xsl:value-of, as you already know how to do. Example:
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select <xsl:value-of select="$var1"/>.* from <xsl:value-of select="$var1"/>
</xsl:template>
Alternatively, you could build the whole select command in one xsl:value-of with use of the concat() function.
Unless you move to XSLT 3.0 (https://www.w3.org/TR/xslt-30/#text-value-templates) where you can do e.g. <xsl:template match="foo" expand-text="yes">select {#att_val1}.* from {#att_val1}</xsl:template> you will have to use your second option or perhaps a <xsl:template match="foo"><xsl:value-of select="concat('select ', #att_val1, '.* from ', #att_val1)"/></xsl:template>, but there is certainly no way in XSLT 1.0 to avoid the use of xsl:value-of completely.

xslt select="." vs select="<tag name>"

I am finally beginning to understand how xslt works.
Since I will be creating several more xslts in the future I would like to write them well.
I am wondering whether there is a preferred way to get the data of an xml tag.
Is it better to use select="." select=" tag name " or is it irrelevant?
For example:
<xsl:value-of select="." />
or
<xsl:value-of select="Vert_Prism" />
To get the data enclosed in the Vert_Prism tag.
<Vert_Prism>1.5</Vert_Prism>
Thanks,
It depends on your context. If your current node is Vert_Prism, then you would use <xsl:value-of select="." /> to get the text value of the current node.
OTOH, <xsl:value-of select="Vert_Prism" /> is an abbreviation of <xsl:value-of select="child::Vert_Prism" /> - so this would not return anything, unless the current Vert_Prism has a child element with the same name. However, it would work fine from the context of the parent node of Vert_Prism.

Setting an XSL variable to a node-set using conditions

This question is very similar to XSL store node-set in variable. The major difference is that if XPath does not find a node which matches the filter, I would like to return the first unfiltered result.
The code I have here works, but I feel like it is a hack and not good XSL style. In this case, each chapter node is identified by a string id. The variable showChapter is the string identifying a chapter. If no chapter is found with this id attribute, I want to return the first chapter.
Relevant code:
<xsl:param name="showChapter" />
<!-- if $showChapter does not match any chapter id attribute,
set validShowChapter to id of first chapter.
-->
<xsl:variable name="validShowChapter">
<xsl:choose>
<xsl:when test="/book/chapter[#id=string($showChapter)][position()=1]">
<xsl:value-of select="$showChapter" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/book/chapter[position()=1]/#id" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- I want $chapter to be a valid node-set so I can use it in
XPath select statements in my templates
-->
<xsl:variable
name="chapter"
select="/book/chapter[#id=string($validShowChapter)][position()=1]"
>
Is this approach as poor of a hack as I think it is, and if so could you point me to a better solution? I am using XSLT 1.0 processed by PHP5's XSLTProcessor, but XSLT 2.0 solutions are welcome.
The following should work. A lot of the usage of position() and string() in your example were unneeded, btw:
<xsl:param name="showChapter" />
<xsl:variable name="foundChapter" select="/book/chapter[#id = $showChapter]" />
<!-- Will select either the first chapter in $foundChapter, or
the first chapter available if $foundChapter is empty -->
<xsl:variable name="chapter"
select="($foundChapter | /book/chapter[not($foundChapter)])[1]" />

XSLT for-each counter - how to access data

for performance testing purposes I want to take a small XML file and create a bigger one from it - using XSLT. Here I plan to take each entity (Campaign node in the example below) in the original XML and copy it n times, just changing its ID.
The only way I can think of to realize this, is a xsl:for-each select "1 to n". But when I do this I do not seem to be able to access the entity node anymore (xsl:for-each select="campaigns/campaign" does not work in my case). I am getting a processor error: "cannot be used here: the context item is an atomic value".
It seems that by using the "1 to n" loop, I am loosing the access to my actual entity. Is there any XPath expression that gets me access back or does anyone have a completely different idea how to realize this?
Here is what I do:
Original XML
<campaigns>
<campaign id="1" name="test">
<campaign id="2" name="another name">
</cmpaigns>
XSLT I try to use
<xsl:template match="/">
<xsl:element name="campaigns">
<xsl:for-each select="1 to 10">
<xsl:for-each select="campaigns/campaign">
<xsl:element name="campaign">
<xsl:copy-of select="#*[local-name() != 'id']" />
<xsl:attribute name="id"><xsl:value-of select="#id" /></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
Define a variable as the first thing in the match, like so:
<xsl:variable name="foo" select="."/>
This defines a variable $foo of type nodeset. Then access it like this
<xsl:for-each select="$foo/campaigns/campaign">
...
</xsl:for-each>

XSL: How best to store a node in a variable and then use it in future xpath expressions?

I need to be able to store a node set in variable and then perform more filting/sorting on it afterward. All the examples I've seen of this involve either using XSL2 or extensions neither of which are really an option.
I've a list of hotels in my XML doc that can be sorted/filtered and then paged through 5 at a time. I'm finding though I'm repeating alot of the logic as currently I've not found a good way to store node-sets in xsl variable and then use xpath on them for further filtering/sorting.
This is the sort of thing I'm after (excuse the code written of the top of my head so might not be 100%):
<xsl:variable name="hotels" select="/results/hotels[active='true']" />
<xsl:variable name="3_star_or_less" select="/results/hotels[number(rating) <= 3]" />
<xsl:for-each select="3_star_or_less">
<xsl:sort select="rating" />
</xsl:for-each>
Has anyone got an example of how best to do this sort of thing?
Try this example:
<xsl:variable name="hotels" select="/results/hotels[active='true']" />
<xsl:variable name="three_star_or_less"
select="$hotels[number(rating) <= 3]" />
<xsl:for-each select="$three_star_or_less">
<xsl:sort select="rating" />
<xsl:value-of select="rating" />
</xsl:for-each>
There is no problem storing a node-set in a variable in XSLT 1.0, and no extensions are needed. If you just use an XPath expression in select attribute of xsl:variable, you'll end up doing just that.
The problem is only when you want to store the nodes that you yourself had generated in a variable, and even then only if you want to query over them later. The problem here is that nodes you output don't have type "node-set" - instead, they're what is called a "result tree fragment". You can store that to a variable, and you can use that variable to insert the fragment into output (or another variable) later on, but you cannot use XPath to query over it. That's when you need either EXSLT node-set() function (which converts a result tree fragment to a node-set), or XSLT 2.0 (in which there are no result tree fragments, only sequences of nodes, regardless of where they come from).
For your example as given, this doesn't seem to be a problem. Rubens' answer gives the exact syntax.
Another note, if you want to be able to use the variable as part of an XPath statement, you need to select into the variable with <xsl:copy-of select="."/> instead of <xsl:value-of select="."/>
value-of will only take the text of the node and you wont be able to use the node-set function to return anything meaningful.
<xsl:variable name="myStringVar">
<xsl:value-of select="."/>
</xsl:variable>
<!-- This won't work: -->
<Output>
<xsl:value-of select="node-set($myStringVar)/SubNode" />
</Output>
<xsl:variable name="myNodeSetVar">
<xsl:copy-of select="."/>
</xsl:variable>
<!-- This will work: -->
<Output>
<xsl:value-of select="node-set($myNodeSetVar)/SubNode" />
</Output>