Reputation: 877
So basically I need a function that loops through XML nodes and IF a condition is true, it adds that value to a variable. I am aggregating social posts and need to count how many of each social post is in the feed. Here is my XML:
<feed>
<channel>
<sources>
<source>
<name>Facebook</name>
<count>3</count>
</source>
<source>
<name>Twitter</name>
<count>2</count>
</source>
<source>
<name>Twitter</name>
<count>8</count>
</source>
</sources>
</channel>
</feed>
The catch is the same source can appear multiple times, and I need to add those together. So I would need a twitter count of 10 for the above XML. Here is where I am at so far:
<xsl:variable name="num_tw">
<xsl:for-each select="feed/channel/sources/source">
<xsl:choose>
<xsl:when test="name, 'twitter')">
<xsl:value-of select="count"/>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="num_fb">
<xsl:for-each select="feed/channel/sources/source">
<xsl:choose>
<xsl:when test="name, 'facebook')">
<xsl:value-of select="count"/>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
This doesn't work because if there is two twitter feeds it puts the numbers side by side and outputs "28" instead of "10". Any help is appreciated!
Upvotes: 1
Views: 5632
Reputation: 70648
You don't need to iterate over the nodes with xsl:for-each here. Instead you can just make use of the sum operator. For example, your num_tw variable can just be re-written like so
<xsl:variable name="num_tw" select="sum(feed/channel/sources/source[name='Twitter']/count)"/>
However, do you really want to hard-code your feed names here? This is really a 'grouping' issue, and in XSLT 1.0 you use a technique called Muencian Grouping to solve it. In your case, you want to group source elements by their name element, and so you define a key like so:
<xsl:key name="source" match="source" use="name" />
Then, you look at all the source elements, and pick the one that is first in the group for their given name element:
<xsl:apply-templates
select="feed/channel/sources/source[generate-id() = generate-id(key('source', name)[1])]" />
Then, within the template that matches this, you can sum up the counts like so:
<xsl:value-of select="sum(key('source', name)/count)" />
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="source" match="source" use="name" />
<xsl:template match="/">
<xsl:apply-templates select="feed/channel/sources/source[generate-id() = generate-id(key('source', name)[1])]" />
</xsl:template>
<xsl:template match="source">
<source>
<xsl:copy-of select="name" />
<count><xsl:value-of select="sum(key('source', name)/count)" /></count>
</source>
</xsl:template>
</xsl:stylesheet>
When applied to your XML, the following is output:
<source>
<name>Facebook</name>
<count>3</count>
</source>
<source>
<name>Twitter</name>
<count>10</count>
</source>
Note that if you did really want to find out the count of a specific feed, like 'Facebook' it would still be preferably to use the key here. For example:
<xsl:variable name="num_fb" select="sum(key('source', 'Facebook')/count)"/>
Upvotes: 4