global variables in xsl:template match patterns? - xslt

Is it possible in XSLT 2.0 to use node sets stored in global variables in xsl:template match patterns?
For instance:
<!-- GLOBAL PARAMETERS -->
<xsl:param name="tktDocRS" />
<xsl:variable name="tktDoc" select="saxon:parse($tktDocRS)" />
...
<xsl:template match="$tktDoc//someNodeInTktDoc">
...
</xsl:template>

XSLT 3.0 (unpublished!) makes the following legal:
<xsl:template match="$x//a">
which will match any a element that has $x as an ancestor.
In XSLT 2.0 you have to write this as:
<xsl:template match="a[ancestor::node() intersect $x]">

Is it possible in XSLT 2.0 to use node sets stored in global variables
in xsl:template match patterns.
Yes, but a variable reference can only occur in the predicate.
According to the eight syntax rules in the W3C XSLT 2.0 specification,
a pattern is a union of *relativePathPattern*s (that can eventually be pre-pended by '/' or '//') and each relativePathPattern consists of *patternStep*s, each of which has the following syntax:
[4] PatternStep ::= PatternAxis? NodeTest PredicateList
both the PatternAxis and the NodeTest cannot contain the $ character, which means that variable references are allowed only in the predicates.
Here is a very simple example:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vOdds" select="/*/*[. mod 2 = 1]"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="num[. = count($vOdds)]">
<num special="yes"><xsl:value-of select="."/></num>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num special="yes">05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>

I think you can't put the variable reference as a step in a pattern. And you don't have to, it should suffice to use
<xsl:template match="someNodeInTktDoc">...</xsl:template>
then you simply need to make sure you apply-templates on $tktDoc.

Related

XPath relative to variable's location in source document

When iterating through the nodes in a node-set variable I want an XPATH 1.0 expression that returns all ancestors of a node (e.g., of $myVariable[7]) -- not the ancestors in the node-set variable, but ancestors in the original document.
I thought one of these would work, but neither does.
select="//*[generate-id()=generate-id($myVariable[7])]/ancestor::*"
select="id(generate-id($myVariable[7]))/ancestor::*"
Am I close?
Edit: It's not central to my question, but I had //ancestor; that extra slash is unnecessary.
Your expression:
//*[generate-id()=generate-id($myVariable[7])]/ancestor::*
is correct.
The reason it is "not working" may be due to the fact that $myVariable[7] doesn't contain what you are expecting.
Here is a simple complete example using the above expression and producing the expected. correct results:
<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="myVariable"
select="/*/*/*/*/num"/>
<xsl:template match="/">
<xsl:for-each select=
"//*[generate-id()
=
generate-id($myVariable[7])
]
/ancestor::*
">
<xsl:value-of select="name()"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<a>
<b>
<c>
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
</c>
</b>
</a>
the wanted, correct result (the names of all ancestors of $myVariable[7]) is produced:
a
b
c
nums

muenchian grouping

I was wondering how this predicate([1]), is hardcoded as 1 always in the muenchian grouping. The concept was not clear for me, after a lot of search. It is explained as the current node, is compared with the 1st group returned by the key.
Why does it always compare with the first one that a key is matched?
Also why are we giving contact[count(. | key('contacts-by-surname', surname)[1]) = 1], the =1 part? again 1 is hardcoded. I referred the below link
http://www.jenitennison.com/xslt/grouping/muenchian.html
Let's say we have a key definition <xsl:key name="contacts-by-surname" match="contact" use="surname"/>, then the expression key('contacts-by-surname', 'Doe') gives you a node set with all contact elements where the surname is Doe. The expression key('contacts-by-surname', 'Doe')[1] gives you the first contact in that "group".
Now when processing all contact elements with for-each or apply-templates we usually want a way to identify the first contact element in each group. This can be achieved with <xsl:for-each select="contact[count(. | key('contacts-by-surname', surname)[1]) = 1]"> or <xsl:for-each select="contact[generate-id() = generate-id(key('contacts-by-surname', surname)[1])]">.
If your requirement is different and you for instance wanted to identify the last item in each group then you could of course use a different predicate, as in <xsl:for-each select="contact[count(. | key('contacts-by-surname', surname)[last()]) = 1]"> or <xsl:for-each select="contact[generate-id() = generate-id(key('contacts-by-surname', surname)[last()])]">.
I was wondering how this predicate([1]), is hardcoded as 1 always in
the muenchian grouping.
This is simple:
The key() function produces all nodes for a given group, and we want to take just one node from any group.
It isn't guaranteed that all groups will have two or more nodes in them -- some might have just one node.
This is why it is safe and convenient to take the first (and possibly the only) node from each group.
We could equally well do the grouping taking the last node from each group (but this will be less efficient):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kNumByMod3" match="num"
use=". mod 3"/>
<xsl:template match=
"num[generate-id()
=
generate-id(key('kNumByMod3', . mod 3)[last()])
]
">
3k + <xsl:value-of select=". mod 3"/>:
<xsl:text/>
<xsl:copy-of select="key('kNumByMod3', . mod 3)"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
produces the wanted, correctly grouped result:
3k + 2:
<num>02</num>
<num>05</num>
<num>08</num>
3k + 0:
<num>03</num>
<num>06</num>
<num>09</num>
3k + 1:
<num>01</num>
<num>04</num>
<num>07</num>
<num>10</num>
The basic algorithm is that there are two nested loops. The outer loop selects one representative node from each group, and the inner loop selects all the nodes in that group (including the one chosen as representative). The easiest way of selecting one representative node from a group is to select the first, hence the predicate [1].

How to do an 'and' statement in XSLT select?

What is an example of a compound select statement using an AND operator, similar to an if statement where condition 1 = true and condition 2 = true?
Here is one of the simplest possible examples:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match=
"num[. mod 2 = 0 and . mod 3 = 0]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when this transformation is applied on this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<num>06</num>
Here is an example of how to use a compound select statement....
<?xml version="1.0" encoding="ISO-8859-1"?>
<A>
<B>
<C>1</C>
<D>2</D>
</B>
<B>
<C>1</C>
<D>3</D>
</B>
<E>test</E>
</A>
and your current template match is for "E", then try the below code to select only B where C = 1 and D = 3: for C and D read condition 1 = true and condition 2 = true
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="E">
<xsl:value-of select="../B[C = 1][D = 3]"></xsl:value-of>
</xsl:template>
<xsl:template match="C"/>
<xsl:template match="D"/>
</xsl:stylesheet>
Good Luck
Use the AND operator:
<xsl:if test="a and b">

xsl:key key() function lookup greater than/less than

Is there a way to do a key() lookup using greaterthan/ lessthan?
example: key('akeyname', <8) would return all nodes with the key string value less than 8.
Is there a way to do a key() lookup
using greaterthan/ lessthan?
example: key('akeyname', <8) would
return all nodes with the key string
value less than 8
No, because the second argument of the key() function must be an expression, but "<8" isn't a syntactically legal XPath expression.
The closest to what you want:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kLT8" match="num" use="not(. >= 8)"/>
<xsl:template match="/">
<result>
<xsl:copy-of select="key('kLT8', 'true')"/>
</result>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<nums>
<num>01</num>
<num>05</num>
<num>03</num>
<num>04</num>
<num>08</num>
<num>06</num>
<num>07</num>
<num>02</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<result>
<num>01</num>
<num>05</num>
<num>03</num>
<num>04</num>
<num>06</num>
<num>07</num>
<num>02</num>
</result>
A more flexible solution is the use of Higher Order Functions (HOFs) in XSLT, which has been implemented for years by the FXSL library (written entirely in XSLT).
Here is the solution using HOFs:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/">
<xsl:import href="../f/func-Operators.xsl"/>
<xsl:import href="../f/func-filter.xsl"/>
<xsl:param name="pLimit" as="xs:integer" select="8"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:sequence select="f:filter(*/number(), f:gt($pLimit))"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), the wanted, correct result is produced:
1 5 3 4 6 7 2
Note: HOFs will become a standard feature of XPath/XSLT/XQuery with the forthcoming version 3.0.

Two phase processing: Do not output empty tags from phase-1 XSLT 2.0 processing

I have some complex XSLT 2.0 transformations. I'm trying to find out if there is general purpose way to ensure that no empty tags are output. So... conceptually, a final stage of processing that recursively removes all empty tags. I understand this could be done by a separate XSLT that did nothing but filter out empty tags, but I need to have it all packaged together in a single one.
This XSLT 2.0 transformation illustrates how multi-pass (in this case 2-pass) processing can be done:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:apply-templates select="$vPass1/*" mode="non-empty"/>
</xsl:template>
<xsl:template match="text()[xs:integer(.) mod 2 eq 0]"/>
<xsl:template match="*[not(node())]" mode="non-empty"/>
</xsl:stylesheet>
when applied on this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
It creates a result document in the first pass (which is captured in the $vPass1 variable), in which all <num> elements with contents even integer are stripped off their content and are empty. Then, in the second pass, applied in a specific mode, all empty elements are removed.
The result of the transformation is:
<nums>
<num>01</num>
<num>03</num>
<num>05</num>
<num>07</num>
<num>09</num>
</nums>
Do note the use of modes, and the special modes #all and #current.
Update: The OP now wants in a comment to delete "recursively" "all nodes that have no non-empty descendant".
This can be implemented simpler using no explicit recursion. Just change:
<xsl:template match="*[not(node())]" mode="non-empty"/>
to:
<xsl:template match="*[not(descendant::text())]" mode="non-empty"/>