Reputation: 13
I need to have in each 'a', all the 'b' that have @n greater than or equal to the @n of the 'a' in which they are contained.
I am using XSLT 2.0 and Saxon-HE 9.6.0.5
XML source:
<blabla>
<a n="2"></a>
<a n="6"></a>
<b n="6"></b>
<b n="1"></b>
<b n="4"></b>
</blabla>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="2.0">
<xsl:template match="blabla">
<all>
<xsl:for-each select="//a">
<a>
<xsl:attribute name="n" select="./@n"/>
<xsl:for-each select="//b">
<xsl:if test="./@n[. >= //a/@n]">
<b>
<xsl:attribute name="n" select="./@n"/>
</b>
</xsl:if>
</xsl:for-each>
</a>
</xsl:for-each>
</all>
</xsl:template>
What I would like to have is:
<all>
<a n="2">
<b n="6"/>
<b n="4"/>
</a>
<a n="6">
<b n="6"/>
</a>
</all>
What I have instead is:
<all>
<a n="2">
<b n="6"/>
<b n="4"/>
</a>
<a n="6">
<b n="6"/>
<b n="4"/>
</a>
</all>
I am not sure if the whole approach is wrong or if I have to adjust something.
Just for completeness, this is the function with which I was trying to do the same thing. The output is NOTHING when I create elements inside the function:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:foo="http://whatever">
<xsl:function name="foo:test">
<xsl:param name="a"/>
<xsl:param name="b"/>
<xsl:for-each select="$a">
<a>
<xsl:attribute name="n">
<xsl:value-of select="$a"/>
</xsl:attribute>
<xsl:if test="$b >= $a">
<b>
<xsl:attribute name="n">
<xsl:value-of select="$b"/>
</xsl:attribute>
</b>
</xsl:if>
</a>
</xsl:for-each>
</xsl:function>
<xsl:template match="/">
test 1: <xsl:value-of select="foo:test(//a/@n, //b/@n)"/>
test 2: <xsl:value-of select="foo:test(7, 6)"/>
test 3: <xsl:value-of select="foo:test(3, 6)"/>
</xsl:template>
The same function, without creating elements, works fine with numbers, but not if I put as parameters the xpath expression that match the source document (it outputs everything).
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:foo="http://whatever">
<xsl:function name="foo:foo:test">
<xsl:param name="a"/>
<xsl:param name="b"/>
<xsl:if test="$b >= $a">
a: <xsl:value-of select="$a"/>
b: <xsl:value-of select="$b"/>
</xsl:if>
</xsl:function>
<xsl:template match="/">
test 1: <xsl:value-of select="foo:test(//a/@n, //b/@n)"/>
test 2: <xsl:value-of select="foo:test(7, 6)"/>
test 3: <xsl:value-of select="foo:test(3, 6)"/>
</xsl:template>
</xsl:stylesheet>
Output:
test 1:
a: 2 6
b: 6 1 4
test 2:
test 3:
a: 3
b: 6
I don't need to do it with a function; if you have suggestions for doing it without a function, it is fine.
Upvotes: 1
Views: 137
Reputation: 7519
Here is an XSL 1.0 solution (also works with XSL 2.0 and is compatible with any XSL processor) that uses multiple templates and parameter passing.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:template match="/blabla">
<all>
<xsl:apply-templates select="a" mode="level-1">
<xsl:sort data-type="number" order="ascending" select="@n" />
</xsl:apply-templates>
</all>
</xsl:template>
<xsl:template match="a" mode="level-1">
<xsl:variable name="threshold" select="@n" />
<a>
<xsl:attribute name="n"><xsl:value-of select="$threshold" /></xsl:attribute>
<xsl:apply-templates select="//b" mode="level-2">
<xsl:with-param name="threshold" select="$threshold" />
</xsl:apply-templates>
</a>
</xsl:template>
<xsl:template match="b" mode="level-2">
<xsl:param name="threshold" />
<xsl:if test="number(@n) >= number($threshold)">
<b>
<xsl:attribute name="n"><xsl:value-of select="@n" /></xsl:attribute>
</b>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 163360
Try:
<xsl:template match="blabla">
<all>
<xsl:for-each select="a">
<a n="{@n}">
<xsl:copy-of select="../b[@n >= current()/@n]"/>
</a>
</xsl:for-each>
</all>
</xsl:template>
Upvotes: 1