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

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.

Related

how to check duplicate element values using xslt

<BookList>
<Book>
<History>
<Type>history</Type>
<Prize>123</Prize>
<Publication>``
<Name>YEAP1</Name>
</Publication>
<RNumber Type="VolumeNumber">11111</RNumber>
<RNumber Type="SupplementNumber">123456</RNumber>
</History>
<chemistry>
<Type>chemistry</Type>
<Prize>333</Prize>
<Publication>
<Name>YEAP</Name>
</Publication>
<RNumber Type="VolumeNumber">11111</RNumber>
<RNumber Type="SupplementNumber">45454</RNumber>
</chemistry>
......
</Book>
</BookList>
There are duplicate VolumnNumber 11111. How to check duplicate VolumnNumber in BoolList xml using xslt . please help on this
I. This can be found using a single XPath expression:
false()
or
/*/*/*/RNumber
[#Type='VolumeNumber'
and
. = ../preceding-sibling::*
/RNumber[#Type='VolumeNumber']
]
Here is a complete XSLT transformation that evaluates this XPath expression and outputs the result of this evaluation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
Duplicate volume numbers exist: <xsl:text/>
<xsl:value-of select=
"false()
or
/*/*/*/RNumber
[#Type='VolumeNumber'
and
. = ../preceding-sibling::*
/RNumber[#Type='VolumeNumber']
]
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<BookList>
<Book>
<History>
<Type>history</Type>
<Prize>123</Prize>
<Publication>``
<Name>YEAP1</Name>
</Publication>
<RNumber Type="VolumeNumber">11111</RNumber>
<RNumber Type="SupplementNumber">123456</RNumber>
</History>
<chemistry>
<Type>chemistry</Type>
<Prize>333</Prize>
<Publication>
<Name>YEAP</Name>
</Publication>
<RNumber Type="VolumeNumber">11111</RNumber>
<RNumber Type="SupplementNumber">45454</RNumber>
</chemistry>
......
</Book>
</BookList>
the wanted, correct result is produced:
Duplicate volume numbers exist: true
II. Solution using keys (generally more efficient):
<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="kVolNum" match="RNumber[#Type='VolumeNumber']"
use="."/>
<xsl:template match="/">
Duplicate volume numbers exist: <xsl:text/>
<xsl:value-of select=
"false()
or
/*/*/*/RNumber
[#Type='VolumeNumber'
and
key('kVolNum',.)[2]
]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), the same correct result is produced:
Duplicate volume numbers exist: true

global variables in xsl:template match patterns?

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.

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

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

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