XSLT/XPath quoting: XPath within an XPath-Function within XSLT - xslt

Here we're using double quotes at the XSLT level; single quotes at the XPath function level, but when we need to embed an XPath expression within the XPath function, how do we quote that?
<xsl:value-of select="document('price-list.xml','/im_prices/row/UPC_Code[text()='abc']"/>
^^^^^
(scroll to the right)

Not sure about that, I have always used the node-set form of:
<xsl:value-of select="document('price-list.xml')/im_prices/row/UPC_Code[text()='abc']"/>
but that may be vendor specific (however, have never known it not to work!)

The second argument of document() is a node-set, not a string, so the problem should not arise. However, there are of course other cases where it can arise.
In XSLT 2.0:
if you want to include the attribute delimiter in a string, escape it as " or &apos;
if you want to include the string delimiter in a string, double it as '' or "".
In XSLT 1.0, the second option is not available. You can get around it either
(a) by swapping the character used for the attribute delimiter and the string delimiter
(b) by using concat() to build the string

Related

What escape character for fn:replace function

I have to to change a bad char to a quotation mark but I can't escape this last one.
Doing this doesn't work
<xsl:value-of select="fn:replace(prog:intitules/prog:intitule_fr,'¿', '\'')"/>
it produces
net.sf.saxon.trans.XPathException: Unmatched quote in expression
Same error with double or triple escapes '\' or '\\'.
My editor refuses this alternative syntax:
<xsl:value-of select='fn:replace(prog:intitules/prog:intitule_fr,"¿", "'")'/>
Any idea ?
Bernard
Try it this way:
<xsl:value-of select='replace(input, "¿", "&apos;")'/>
In XPath 2.0+, you can escape an apostrophe within an apostrophe-delimited string literal by doubling it. So try:
''''
You need to think very carefully about escapes when you're using regular expressions within XPath within XSLT. Why does the character need escaping?
If it has a special meaning in regular expressions (for example '(') then use a backslash
If it isn't allowed because of XPath rules (like here), use XPath escaping (write 'O'Neil' as 'O''Neil' or "a="3"" as "a=""3""")
If it isn't allowed because of XML rules (e.g. "<"), use XML escaping (write < as <)
The reason this doesn't work:
<xsl:value-of select='fn:replace(prog:intitules/prog:intitule_fr,"¿", "'")'/>
is that the XML parser is treating the apostrophe within the string literal as marking the end of the value of the select attribute. So here you have an XML issue, and under rule 3 you therefore need to use XML escaping (&apos;)
Assuming your files are utf-8 encoded, you could you try a workaround, using the Unicode apostrophe character (hexadecimal 2BC) instead of quote (hexadecimal 27):
<xsl:value-of select="fn:replace(prog:intitules/prog:intitule_fr,'¿', 'ʼ')"/>
Edited: searching a little bit more, I discovered that switching ' and " and using the entity &apos; will get the same result, as michael.hor257k proposed meanwhile:
<xsl:value-of select='fn:replace(prog:intitules/prog:intitule_fr,"¿", "&apos;")'/>

Xpath: pass an 'include everything' parameter to [starts-with(...)]

Using Saxon HE/Xpath 2
To filter my xml file of a list of businesses by 'BusinessType' I pass a parameter (ie "Restaurant" or "School") to the xsl file:
"java.exe" -jar "saxon9he.jar" -s:FHRS.xml -xsl:FHRS.xsl BusinessType="Restaurant"
The BusinessType category names are quite long so to save typing I pass the parameter to starts-with:
<xsl:param name="BusinessType"/>
<xsl:variable name="ID_New" select="//Details[starts-with(BusinessType,$BusinessType)]/FHRSID"/>
This is just a one line example. The same format is used multiple times in the xsl routine.
Is it possible to pass an 'return everything' wildcard parameter to $BusinessType? I've tried * and .*. I'm not well versed in RegEx.
The starts-with function https://www.w3.org/TR/xquery-operators/#func-starts-with works only with strings, not with regular expressions. However an empty sequence or an empty string as the second argument means the function returns true so you could simply define <xsl:param name="BusinessType" select="()"/> and then, if you don't want to filter, you don't set the parameter and the predicate starts-with(BusinessType,$BusinessType) is always true.
If you want to use regular expressions you need to use the matches function e.g. matches(BusinessType,$BusinessType) where a pattern to match any starting character would be if you set the param to ^.*.

How to express a string containing both apostrophes and quotes in XPath?

I'll spare you the details because they would be needlessly confusing. Long story short, I'm using XSLT 1.0 to generate XSL documents, I'm trying to compare a variable to a literal string, and that string may contain quotes and apostrophes.
For the sake of simplicity, let's say that this literal is composed of two characters: a quote followed by an apostrophe. In reality, it can be any text really. Is there a simpler way to do this:
<xsl:if test="$var = concat('"', "'")">
than this?
<xsl:variable name="str">"'</xsl:variable>
<xsl:if test="$var = $str">
I have checked XPath's specs and there doesn't seem to be a way to escape characters, so the following would not work as desired:
<xsl:if test="$var = '"&apos;'">
Thanks!
There's no way to do it neatly in XPath 1.0. In XPath 2.0, you can escape both kinds of quotes by doubling.
& quot;& amp;&(!)apos; -looks much better, but what did you want to get?
In anyway: once I have written application that deals with producing of Javascript over XSLT.
The same problem with huge number of & quot;,... we solved in 2 ways:
Declare global xsl:param, $q - looks shorter than & quot;
Use 'translate' XPath function, make assumption '!' - is a & quot;, # is a & amp; ..

XSLT/XPath : No upper-case function in MSXML 4.0?

I try to use upper-case() in an XPATH, my parser is MSXML 4.0, and I get :
upper-case is not a valid XSLT or XPath function.
Is it really not implemented ?
There are no functions in xslt 1.0 to convert to uppercase or lowercase. Instead do the following:
If it is required in a lot of places:
Declare these two xsl variables (this is to make the xslt more readable)
<!-- xsl variables up and lo and translate() are used to change case -->
<xsl:variable name="up" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="lo" select="'abcdefghijklmnopqrstuvwxyz'"/>
And use them in your translate function to change the case
<xsl:value-of select="translate(#name,$lo,$up)"/>
If you need to use it in just one place, no need to declare variables
<xsl:value-of select="translate(#name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
Maybe this can help you:
translate(string, string, string)
The translate function takes a string and, character-by-character, translates characters which match the second string into the corresponding characters in the third string. This is the only way to convert from lower to upper case in XPath. That would look like this (with extra white space added for readability). This code would translate the employee last names to upper case and then select those employees whose last names begin with A.
descendant::employee[
starts-with(
translate(#last-name,
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
"A"
)
]
If the second string has more characters than the third string, these extra characters will be removed from the first string. If the third string has more characters than the second string, the extra characters are ignored.
(from http://tutorials.beginners.co.uk/professional-visual-basic-6-xml-part-1-using-xml-queries-and-transformations.htm?p=3)

lower case the first character of a string using only xslt 1.0

I have seen patterns for translating a string into lower (or upper case) using the translate function for folks stuck using xslt 1.0.
Is there a elegant way of just making the first letter of a string lowercase?
TestCase => testCase
If your string were, for example, in an attribute called name:
<xsl:value-of select="concat(translate(substring(#name, 1, 1), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), substring(#name, 2))"/>
You should be able to combine substring and concat with translate to do it like so:
concat(translate(substring(s,1,1), $smallcase, $uppercase),substring(s,2))
Use the XPath translate function, having separated the string into first character and the rest. This will require somewhat long winded XSLT using multiple variables to hold intermediate results.
XSLT has a substring function, so you could use that pattern with the substring function to get what you want.