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
Related
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.
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">
So here is my problem...
I have 2 xsl transformations and they both have xsl:for-each in them as a starting point.
I need to create one (master) xslt which will call them. But of course, there is a catch.
This new xslt should give output one node from first xslt, then one node from second.(both xslt have EmployeeId, but are basically different reports that have to be printed one after another).
Because these existing xslt's have for-each in them, when I include them in this master xslt I get an output: all nodes from first xslt, then all nodes from second.
Also this 2 xslt's have to be backward compatible, so they should work as before if they are not called from this master template.
I'm a starter to xslt and I managed to create some reports when working with only one xslt,but I can't seem to find the solution to this problem, so I appreciate all the help I can get.
I was thinking of creating a new xslt that would be a mix of two that I have, but this was ruled out by my boss.
Thanks,
Benxy
EDIT:
Here goes some examples as requested:
This is the first xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="4.0" encoding="windows-1250" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/" name="testXslt1">
<xsl:for-each select="a">
<table>
<tr>
<td>
<xsl:value-of select="#SomeData"></xsl:value-of>
</td>
etc.
</tr>
<xsl:apply-templates select="b" mode="tmp"/>
</table>
</xsl:for-each>
</xsl:template>
<xsl:template match="node()" mode="tmp">
<tr >
<td><xsl:value-of select="#SomeOtherData"></xsl:value-of></td>
etc.
</tr>
</xsl:template>
Second xslt is similar to first one.
In Master xslt I would import both of them and in for each call templates testXslt1 and testXslt2.
This new xslt should give output one
node from first xslt, then one node
from second
Here is a simple example how this can be done:
This transformation combines the results of two templates that are applied on the XML document.
The first templates produces twice the value of every /*/* node. The second transformation produces the square of the value of every /*/* node. The results of applying the two remplates each on the XML document are mixed together in alternation as required, and this is the final result:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfResults1">
<xsl:apply-templates select="/"
mode="transform1"/>
</xsl:variable>
<xsl:variable name="vrtfResults2">
<xsl:apply-templates select="/"
mode="transform2"/>
</xsl:variable>
<xsl:variable name="vResults1"
select="ext:node-set($vrtfResults1)/node()"/>
<xsl:variable name="vResults2"
select="ext:node-set($vrtfResults2)/node()"/>
<xsl:for-each select="$vResults1">
<xsl:variable name="vPos" select="position()"/>
<xsl:copy-of select=".|$vResults2[$vPos]"/>
</xsl:for-each>
<xsl:if test=
"count($vResults2) > count($vResults2)">
<xsl:copy-of select=
"$vResults2[position()>count($vResults1)]"/>
</xsl:if>
</xsl:template>
<xsl:template match="/" mode="transform1">
<xsl:for-each select="/*/*">
<xsl:copy>
<xsl:value-of select=".+."/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:template match="/" mode="transform2">
<xsl:for-each select="/*/*">
<xsl:copy>
<xsl:value-of select=".*."/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</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>2</num>
<num>1</num>
<num>4</num>
<num>4</num>
<num>6</num>
<num>9</num>
<num>8</num>
<num>16</num>
<num>10</num>
<num>25</num>
<num>12</num>
<num>36</num>
<num>14</num>
<num>49</num>
<num>16</num>
<num>64</num>
<num>18</num>
<num>81</num>
<num>20</num>
<num>100</num>
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.
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"/>