Reputation: 67
I hava a list of elements A:
<a sign="0">100<a/>
<a sign="1">150<a/>
<a sign="2">200<a/>
<a sign="0">250<a/>
<a sign="3">100<a/>
and another list of elements B:
<b>
<sign>1</sign>
<sign>3</sign>
</b>
<b></b> <!-- no sign -->
First, for each < b >, I want to sum up all < a > values that have the same sign then b. I do this with recursion:
<xsl:template name="sumCount">
<xsl:param name="nodes" /> <!-- the a elements -->
<xsl:param name="signs"/> <!-- the signs of the b element -->
<xsl:param name="tempSum" select="0" />
<xsl:choose>
<xsl:when test="$nodes and $signs">
<xsl:variable name="countSum" select="sum($nodes[@sign=$signs[1]])" />
<xsl:call-template name="sumItems">
<xsl:with-param name="nodes" select="$nodes" />
<xsl:with-param name="tempSum" select="number($tempSum) + number($countSum)" />
<xsl:with-param name="signs" select="$signs[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$tempSum" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
But now, if < b > has no signs, I want to find the sum all elements from A which have not a sign that is a sign of one of the sibblings of my actual < b > node. (Simply said, I want to find exactly all other nodes, that will not be picked up by the first summarize).
So in this example, for the second < b > with no sign, I would want to find < a > with sign 0 and 2 (and all other numbers other then 1 and 3). I could select all other < b > nodes, but then I would need something like:
select="sum($nodes[@sign *not in* $otherBNodes)"
IS there something like this in xslt 1.0? How can I achieve this?
Edit: As the original answer does not solve my problem completly, but answered the question correctly, I will add the extending context here.
The problem is that my < a > nodes are not written in the root xml, but generated in the xsl and then made accessable by ext:node-set. I did edit your answer to demonstrate this.
XML:
<root>
<b id="1">
<sign>1</sign>
<sign>3</sign>
</b>
<b id="2"/>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name = "cNodes">
<xsl:element name="a">
<xsl:attribute name="sign"><xsl:value-of select="1"/></xsl:attribute>
<xsl:value-of select="100"/>
</xsl:element>
<xsl:element name="a">
<xsl:attribute name="sign">
<xsl:value-of select="1"/>
<xsl:value-of select="2"/>
</xsl:attribute>
<xsl:value-of select="150"/>
</xsl:element>
<xsl:element name="a">
<xsl:attribute name="sign"><xsl:value-of select="3"/></xsl:attribute>
<xsl:value-of select="200"/>
</xsl:element>
<xsl:element name="a">
<xsl:attribute name="sign"><xsl:value-of select="4"/></xsl:attribute>
<xsl:value-of select="250"/>
</xsl:element>
</xsl:variable>
<xsl:for-each select="ext:node-set(a)"> <!-- switch context-->
<xsl:key name="a" match="a" use="@sign" />
<xsl:variable name="all_a" select="a" />
<xsl:copy>
<b id="1">
<xsl:value-of select="sum(key('a', 1))"/> <!-- I did not think about how to bring b here yet, so I take 1 for now-->
</b>
</xsl:copy>
</xsl:for-each>
Of course this does not work, while I can iterate over the < a > nodes, I somehow cannot create the key.
Upvotes: 1
Views: 75
Reputation: 117083
I would suggest a radically different approach:
XML
<root>
<a sign="0">100</a>
<a sign="1">150</a>
<a sign="2">200</a>
<a sign="0">250</a>
<a sign="3">100</a>
<b id="1">
<sign>1</sign>
<sign>3</sign>
</b>
<b id="2"/>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="a" match="a" use="@sign" />
<xsl:template match="/root">
<xsl:variable name="all_a" select="a" />
<xsl:variable name="matched_a" select="key('a', b/sign)" />
<xsl:copy>
<xsl:for-each select="b">
<b id="{@id}">
<xsl:choose>
<xsl:when test="sign">
<xsl:value-of select="sum(key('a', sign))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="sum($all_a) - sum($matched_a)"/>
</xsl:otherwise>
</xsl:choose>
</b>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<b id="1">250</b>
<b id="2">550</b>
</root>
If you really need to create the a
elements by hard-coding them inside the stylesheet, consider something like:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="a" match="a" use="@sign" />
<xsl:template match="/root">
<xsl:variable name="a-rtf">
<a sign="0">100</a>
<a sign="1">150</a>
<a sign="2">200</a>
<a sign="0">250</a>
<a sign="3">100</a>
</xsl:variable>
<xsl:variable name="a-set" select="exsl:node-set($a-rtf)" />
<xsl:variable name="all_signs" select="b/sign" />
<!-- output -->
<xsl:copy>
<xsl:for-each select="b">
<b id="{@id}">
<xsl:variable name="sign" select="sign" />
<!-- switch context to the variable document -->
<xsl:for-each select="$a-set">
<xsl:choose>
<xsl:when test="$sign">
<xsl:value-of select="sum(key('a', $sign))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="sum($a-set/a) - sum(key('a', $all_signs))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</b>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3