Access matched pattern within XSL tokenize - xslt

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.

Related

XSLT - Check if pattern exists in an element string

I have the following element as part of a larger XML
<MT N="NonEnglishAbstract" V="[DE] Deutsch Abstract text [FR] French Abstract text"/>
I need to do some formatting of the value in #V attribute, only if it contains anything like [DE], [FR] or any two capital letters representing a country code within square brackets.
If no such pattern exist, I need to simply write the value of #V without any formatting.
I can use an XSLT 2.0 solution
I was hoping that I could use the matches() function something like
<xsl:choose>
<xsl:when test="matches(#V,'\[([A-Z]{{2}})\]([^\[]+'">
//Do something
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#V"/>
</xsl:otherwise>
</xsl:choose>
I think all you need is:
matches(#V,'\[[A-Z][A-Z]\]')
You don't have to match the entire string to get a true() ... I tell my students to write as short a reg-ex as possible.
You have not posted anything about what you have tried. How about looking up translate function and translating the strings capital letters to something like "X". Then test that string result for the existence of [XX]. That alone would tell you whether you need to process it.
<xsl:variable name="result">
<xsl:value-of select="translate(#V,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','XXXXXXXXXXXXXXXXXXXXXXXXX')"/>
</xsl:variable>
Then use that result and then test:
contains($result, "[XX]")
No regex required, pure XSL 1.1

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))"/>

XSLT substring and hash tag

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

How can I translate ' into an apostrophe in xslt

The relevant parts of the code:
<xsl:variable name="apos">'</xsl:variable>
<xsl:variable name="and" select='"'"' />
<xsl:value-of select="translate(products_name/node(),$and,$apos)"/>
I'm thinking this should be a simple thing and that the above code should work but it doesn't effect the output at all.
(I used variables because names cannot begin within an ampersand and using just an apostrophe brings up a compile error.)
I've tested the code to make sure the translate is working using strings and there are no errors there.
Any help would be greatly appreciated.
You are on the right track, but not yet there: Your problem is, that XSL is a language that itself is written using XML. For all XML languages, the parser automatically decodes XML entities. The XSLT engine only comes afterwards.
As a result, the XSLT engine neither does nor can distinguish whether you wrote ' or ' - it's the same. For your problem, this has two effects:
You have to use a variable containing the apostrope - this is because the apostrophe itself is reserved for string literals in expressions that may contain functions. Even for <xsl:value-of select="translate(products_name/node(),$and,''')"/>, the XML parser transforms the entity into an apostrophe, i.e. <xsl:value-of select="translate(products_name/node(),$and,''')"/>
You have to escape the ampersand used in the string you search for: for the XSL engine, the variable "and" contains the value ', i.e. you are replacing an apostrophe with an apostrophe.
Working solution:
<xsl:variable name="apos">'</xsl:variable>
<xsl:value-of select='translate(text(), "&#039;", $apos)'/>
Technically, there's no difference in any XML between &apos;, ' and ', they're different ways of representing exactly the same thing. Therefore, that translate call shouldn't do anything.
It depends on how you're transforming it, where that output is (attribute value or element?), and how the output is serialized to text, but your problem isn't with your XSLT.

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.