escaping an ampersand in xsl variable - xslt-1.0

I'm trying to encode certain text variables into a QR code using qrserver.com. (specifically, a gift message on a packing slip)
Here's the snippet of code relevant to the encoding:
<!-- gift message -->
<xsl:for-each select="Customer/Order/Item">
<xsl:for-each select="Option[Name='Gift Message (optional)']">
<xsl:variable name="test" select="translate(Description,'','')" />
<xsl:value-of select="concat('https://api.qrserver.com/v1/create-qr-code/?data=', $test,'&size=100x100&charset-source=UTF-8')" />
</xsl:for-each>
</xsl:for-each>
Occasionally the text will contain an ampersand, like "Merry Christmas & Happy New Year", so the url that is passed is
https://api.qrserver.c-o-m/v1/create-qr-code/?data=Merry Christmas & Happy New Year&size=100x100
and everything after the ampersand gets truncated. The result is a QR code that encodes "Merry Christmas" but no Happy New Year. I tried using the translate function to replace the ampersand with "and", but translate(Description,'&','and') only works on the first letter, so it replaces the ampersand with "a". This is in xsl 1.0, so I don't have the option of the replace function. Does anyone out there know how I can deal with these ampersands? Thanks in advance!

Related

How to put double and single quote in value of AllowedSymbols variable to use in XSLT Translation

Using XSLT 1.0, in the XLST template below, I want to add the single and double quote to the list of allowed values. Getting error on vAllowedSymbols2 saying that "string literal not closed".
<xsl:template name="CleanAlphaField">
<xsl:param name="inputText" />
<xsl:param name="maxLength" />
<xsl:variable name="vAllowedSymbols2" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !#$%()+-_,.;:=[]{}\?"&apos;'"/>
<xsl:variable name="vAllowedSymbols" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !#$%()+-_,.;:=[]{}\?'"/>
<xsl:variable name="truncatedInputText" select="substring($inputText,1,$maxLength)" />
<!-- return the revised string -->
<xsl:value-of select="translate($truncatedInputText,translate($truncatedInputText, $vAllowedSymbols, ''),'')"/>
</xsl:template>
You might need to create these as separate variables for XML escaping reasons.
<xsl:variable name="singleQuote" select='"&apos;"' />
<xsl:variable name="doubleQuote" select="'"'" />
Having done that, you can concat these together
<xsl:variable name="vAllowedSymbols2"
select="concat($vAllowedSymbols, $singleQuote, $doubleQuote)" />
This happens because the XML entity expansion happens before things reach the XSLT processor, so in the minimal case
<xsl:variable name="invalid" select="'&apos;'" />
the value of #select gets expanded and the XSLT engine sees an attribute (name={}select, value=''') and doesn't know that it came from an entity expansion; it just knows that three single quotes doesn't make a valid XPath expression.
You could do simply:
<xsl:variable name="vAllowedSymbols2">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !#$%()+-_,.;:=[]{}\?"'</xsl:variable>

XSLT: variables and "empty" labels

I have an XML datafile containing among other things a string of arbitrarily many comma separated values. I want those values to be displayed in a web browser as a list with one value per line. So I wrote an XSLT template that takes this string, displays the first value followed by a linebreak tag (<br/>), properly name-spaced, and resources with the remainder of the string. In effect, the commas are being replaced by HTML <br/> tags.
Now, when I store the result of calling that template in a xsl:variable, and display that through xsl:value-of, then the HTML tags disappear: what is shown is the string minus the commas.
When I display the result directly by having the xsl:call-template in place of the xsl:value-of, all is fine, and the values appear in a list.
So, what's going on?
Is this behavior an implementation artifact, or is it standard XSLT?
Use xsl:copy-of instead of xsl:value-of if you want to output nodes (like your br elements), xsl:value-of creates a simple text node with the string value(s) selected.
Here is an example that shows the difference between xsl:value-of and xsl:copy-of, you will note that it is not the use of the variable with newly created br elements that makes the difference, it is simply the use of xsl:value-of that creates a text() node with the string conversion of the selection:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>
<xsl:variable name="var">Phrase 1.<br/>Phrase 2.<br/>Phrase 3.</xsl:variable>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<section>
<h1>Example 1: value-of</h1>
<xsl:value-of select="$var"/>
</section>
<section>
<h1>Example 2: copy-of</h1>
<xsl:copy-of select="$var"/>
</section>
<xsl:apply-templates select="//p"/>
<xsl:apply-templates select="//p" mode="copy-of"/>
</body>
</html>
</xsl:template>
<xsl:template match="p">
<section>
<h1>Example 1: value-of</h1>
<xsl:value-of select="."/>
</section>
</xsl:template>
<xsl:template match="p" mode="copy-of">
<section>
<h1>Example 1: copy-of</h1>
<xsl:copy-of select="."/>
</section>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/gWmuiJy/1
Output is
Example 1: value-of
Phrase 1.Phrase 2.Phrase 3.
Example 2: copy-of
Phrase 1.
Phrase 2.
Phrase 3.
Example 1: value-of
Line 1.Line 2.Line 3.
Example 1: copy-of
Line 1.
Line 2.
Line 3.
It seems that you hit the boundaries of the RTF ("Result tree fragment"):
When you use an XML fragment to initialize a variable or a parameter, then the variable or parameter is of the
"result tree fragment" datatype. This is an XSLT 1.0 specific datatype [just like node-set, but slightly different].
A result tree fragment is equivalent to a node-set that contains just the root node.
You cannot apply operators like "/", "//" or predicate on a result tree fragments. They are only applicable for node-set datatypes.
[...]
a) In XSLT 1.0
The resolution of this is to convert the result tree fragment into a node-set. I am not aware of any oracle specific xpath extension functions that can do this trick for you.
You could use EXSLT to achieve this.
b) Use XSLT 2.0
You can code your transformations in XSLT 2.0. XSLT 2.0 deprecates ResultTreeFragments i.e. if you are modeling an XSLT 2.0 transformation, and you create a variable or a parameter that holds a tree fragment, it is implicitly a node sequence.
So without using an XSLT version greater than 1, you're out of luck. So better use XSLT-2.0 or 3.0 to solve this problem.
Is this behavior an implementation artifact, or is it standard XSLT?
It is standard for XSLT-1.0, but not for XSLT-2.0+.

Apache fop generated PDF renders hyperlinks by replacing accented characters with '?'

I have used Apache FOP 1.1 to programmatically generate PDF. The PDF is supposed to contain name of a document as hyperlink. When I click on the name, it should open the corresponding file. Here is the code:
<fo:block>
<fo:basic-link color="blue" show-destination="new">
<xsl:attribute name="external-destination">
<xsl:choose>
<xsl:when test="#parentFolderPath">
<xsl:value-of select="#parentFolderPath" />/<xsl:value-of select="#FileName" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#FileName" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute><xsl:call-template name="writeWithoutOverlap"><xsl:with-param name="data" select="#FileName"/></xsl:call-template>
</fo:basic-link>
</fo:block>
This works perfectly fine when I have a file having English characters in the name. However, when I have a file name like this: "étudiant où forêt naïve garçon.docx" the hyperlink formed in the PDF replaces the accented characters with '?'.
This is a screen shot of the PDF where you can see the malformed hyperlink:
I am using "Arial" font and encoding="UTF-8".
When the name of the file is getting printed correctly, why is the hyperlink giving a problem?

XSLT decimal-format causes exception

I am trying to use the xslt:decimal-format element, but I get the same error message whether I use my own code or the example code provided by w3schools.com. This is the w3 sample code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:decimal-format name="euro" decimal-separator="," grouping-separator="."/>
<xsl:template match="/">
<xsl:value-of select="format-number(26825.8, '#,###.00', 'euro')"/>
</xsl:template>
</xsl:stylesheet>
And this is the XsltException it produces when I run it in Visual Studio 2010:
"Format '#,###.00' cannot have zero digit symbol after digit symbol after decimal point."
What's wrong on my side that causes this error?
You have changed the decimal format, called "euro" so that a valid number looks like this "1.232,99" (one thousand, two hundred and thirty two, point nine-nine in words). This does not match the format you have requested which is "#,###.00".
Change your format-number pattern to "#.###,00"

Can disable-output-escaping be set using a template parameter?

Why won't the following work in XSLT1.0?
<xsl:template name="GenerateSummaryOld">
<xsl:param name="Content" />
<xsl:param name="Length" />
<xsl:param name="DisableOutputEscaping" />
<xsl:value-of select="substring($Content, 1, $Length)" disable-output-escaping="$DisableOutputEscaping" />
<xsl:if test="string-length($Content) > $Length"><i>...text has been shortened</i></xsl:if>
</xsl:template>
I'm using the following when calling the template:
<xsl:with-param name="DisableOutputEscaping">no</xsl:with-param>
I'm trying this in a SharePoint Content Query WebPart but I get a web part error. If I hard-code disable-output-escaping as "yes" or "no" in the template, i get no error.
Short answer: the value of disable-output-escaping must be specified literally in the XSLT stylesheet; it cannot be calculated at stylesheet execution time.
That is, the behavior you are observing is the behavior prescribed by the language definition.
Longer answer: The XSLT 1.0 spec shows the syntax of xsl:value-of like this (more or less):
<!-- Category: instruction -->
<xsl:value-of
select = string-expression
disable-output-escaping = "yes" | "no" />
Note that "string-expression" is italicized here; it means that the select attribute has as its value not the string "string-expression" but any XPath expression which can be evaluated and coerced to a string. But the "yes" and "no" of disable-output-escaping are not italicized, not described as being an expression, and not described as being an attribute-value template. The "yes" or "no" value must be given literally.
The closest the spec comes to saying this explicitly (that I could find) is the note in section 7.6.2 on attribute value templates:
NOTE:Not all attributes are interpreted as attribute value templates. Attributes whose value is an expression or pattern, attributes of top-level elements and attributes that refer to named XSLT objects are not interpreted as attribute value templates. ...
This is one of a number of early-binding constraints in XSLT designed to ensure that stylesheets could be compiled and not just interpreted.
The explanation was provided in the good answer by C. M. Sperberg-McQueen.
Here is a workaround:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vAmp">&</xsl:variable>
<xsl:variable name="vYesNo" select="'yes'"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="$vYesNo = 'yes'">
<xsl:value-of select="$vAmp" disable-output-escaping="yes"/>
</xsl:when>
<xsl:when test="$vYesNo = 'no'">
<xsl:value-of select="$vAmp" disable-output-escaping="no"/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the result is:
&
If we replace:
<xsl:variable name="vYesNo" select="'yes'"/>
with:
<xsl:variable name="vYesNo" select="'no'"/>
the result of the modified transformation now is:
&