XSLT substring and hash tag - xslt

Can someone tell me why this is not working?
Throughout my document I have several column breaks marked as follows: <cb ed="#S" n="45rb"/>
The hash tag is there to refer to another element where the source document is identified.
Now I want to display the column break in the following document. So that it looks like this:
|S45rb|. I thought I could use a simple substring function to get rid of the hash tag like so.
<xsl:template match="TU:cb">
<xsl:variable name="hashms"><xsl:value-of select="//TU:cb/#ed"/></xsl:variable>
<xsl:variable name="ms"><xsl:value-of select="substring($hashms,1,1)"/></xsl:variable>
<span>| <xsl:value-of select="$ms"/> <xsl:value-of select="//TU:cb/#n"/> |</span>
</xsl:template>
When I do it this way I get the following result: |#75ra|. Shouldn't the first 1 in the argument refer to the first character of the string and then the second 1 tell it to move over one character and leave me with the desired S? Instead I don't get the S but only the hash tag. Is there something about hash tags and strings I do not know?
Thanks for your help.

XSL is not 0 base, if you want to start at the second character (after the #), it should be 2 rather than 1.
http://www.w3schools.com/Xpath/xpath_functions.asp
substring($hashms,2,1) = S
You can also omit the length, which might be of benefit for extensibility later on. Especially if whatever comes after the # is the identifier, why limit your code?
substring($hashms,2) = S
substring('#S2',2) = S2

Related

Access matched pattern within XSL tokenize

Suppose I have the following variable
<xsl:variable name="randomString" select="'COLUMN1 == 400 or COLUMN1 == 5 and COLUMN2 != 3'" />
Is there any convenient way to access the matched pattern within the tokenize() function e.g. using this
<xsl:for-each select="tokenize($randomString, 'and|or')">
<xsl:value-of select="concat('not(', current(), ')')" />
<!-- How do I access the matched pattern? -->
</xsl:for-each>
Or do I have to use a custom template like the one I have found here http://docbook.sourceforge.net/release/xsl/1.77.0/doc/lib/str.tokenize.keep.delimiters.html
No, there is no way to retrieve the matched separator. "The separators themselves are not returned." (http://www.w3.org/TR/xpath-functions/#func-tokenize)
A workaround could be, tokenizing with or as a separator in an outer loop, then tokenizing with and as a separator in an inner loop. Then you would always know which separators you were dealing with, based on where in the loops you are.
Another approach would be to use analyze-string(). See this answer.

How to easily generate unique strings in XSLT?

I have a question!
I have an XML document that has sections and subsections. I am generating a Doxygen page out of it using XSLTProc and now I have a problem. When I generate a section name like this:
<xsl:template match="SECTION/SUBSECTION">
#subsection <xsl:value-of select="#title"/>
<xsl:apply-templates/>
</xsl:template>
Then the first word of the title does not show up, because Doxygen expects the declaration in this way:
#subsection <subsectionname> <subsectiontitle>
So, the first word is automatically treated as the subsection name. Putting a randomly generated string there does not seem like a very simple task. I tried to put unique number instead, by using <xsl:value-of select="count(preceding-sibling::*[#col]) + 1", which worked as expected, but as it turns out, Doxygen does not accept numbers as subsection names. I also tried to strip white spaces of "#title" and use that as the subsection name, but XSLTProc complains that it was not an immediate child of <xslt:stylesheet>. How can I easily put some unique string there? It does not have to be meaningful text.
Thanks in advance!
Use the generate-id() function.
<xsl:value-of select="generate-id(#title)"/>
If you want the generated string to be more "readable", here is one way to do this:
<xsl:value-of select="concat(#title, generate-id(#title))"/>

Storing html tags within an xsl variable

Sorry if this is a dumb question, but it is possible to store, and retrieve, a HTML snippet within an xsl 1.0 variable? EG:
<xsl:variable name="something"><p>Hi there</p><p>How are you today?</p></xsl:variable>
<xsl:value-of disable-output-escaping="yes" select="$something"/>
It just when I try, it seems to strip the HTML tags out. Thanks.
You need to use <xsl:copy-of select="$something"/> instead of xsl:value-of.
I'll add some explanation of what's happening :)
The reason you're not getting the html tags is that the $something variable contains a dom fragment, not a string: the value-of element extracts the content of the node(s) the same way as the string() function does, so does not serialize the nodes.
This would provide, instead, a string representation of the html string you have and you can then print it out with value-of and disable-output-escaping:
<xsl:variable name="something"><![CDATA[<p>Hi there</p><p>How are you today?</p>]]></xsl:variable>
(see https://msdn.microsoft.com/en-us/library/ms256181(v=vs.110).aspx "The results are converted to a string, as by a call to the string() function")

apostrophe text comparison in xsl

I have a problem with text with apostrophe symbol
example i try to test this xml having the symbol is then how can i compare ?
<xsl:for each select="country[nation='India's]">
this is statement showing error
Regards
Nanda.A
One way to do it would be:
<xsl:variable name="apos" select='"&apos;"'/>
<!-- ... later ... -->
<xsl:for-each select="country[nation=concat('India', $apos, 's')]">
The problem here is twofold:
XSLT defines no way of character escaping in strings. So 'India\'s' is not an option.
You must get through two distinct layers of evaluation.
These are:
XML well-formedness: The XML document your XSLT program consists of must be well-formed. You cannot violate XML rules.
XSLT expression parsing: The resulting attribute value string (after XML DOM parsing is done) must be make sense to the XSLT engine.
Constructs like:
<xsl:for-each select="country[nation='India's']">
<xsl:for-each select="country[nation='India&apos;s']">
pass the XML layer but violate the XSLT layer, because in both cases the effective attribute value (as stored in the DOM) is country[nation='India's'], which clearly is an XPath syntax error.
Constructs like:
<xsl:for-each select="country[nation=concat('India', "'", 's')]">
<xsl:for-each select="country[nation=concat("India", "&apos;", "s")]">
clearly violate the XML layer. But they would not violate the XSLT layer (!), since their actual value (if the XSLT document could be parsed in the first place) would come out as country[nation=concat('India', "'", 's')], which is perfectly legal as an XPath expression.
So you must find a way to pass through both layer 1 and layer 2. One way is the variable way as shown above. Another way is:
<xsl:for-each select="country[nation=concat('India', "'", 's')]">
which would appear to XSLT as country[nation=concat('India', "'", 's')].
Personally, I find the "variable way" easier to work with.

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)