with xsl I'm trying ...">

Xsl sum sibling values

I have xml:

<people> <man age="20" /> <man age="40" /> <man age="30" /> <man age="80" /> <people> 

with xsl I'm trying to output:

 first age:20 first and second age (combined): 60 first second and third age(combined) :110 first second third and fouth age(combined) :190 

I know how to choose the age, but how can I add and write them down?

Also note that <man> elements can be more than just 4.

+4
source share
3 answers

Ok, I just read that you just need the numbers, so the next one without xslt

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" indent="yes" /> <xsl:variable name="elements" select="/people/man"/> <xsl:variable name="count" select="count($elements)"/> <!-- Main Entry point --> <xsl:template match="/"> <xsl:call-template name="addthem"> <xsl:with-param name="pos" select="1"/> <xsl:with-param name="sum" select="$elements[1]/@age"/> </xsl:call-template> </xsl:template> <!-- recursive calling template to sum up the ages --> <xsl:template name="addthem"> <xsl:param name="pos"/> <xsl:param name="sum"/> <xsl:value-of select="$sum"/> <xsl:text> </xsl:text> <!-- recursive call to sum up the ages --> <xsl:if test="$pos lt number($count)"> <xsl:call-template name="addthem"> <xsl:with-param name="pos" select="$pos + 1"/> <xsl:with-param name="sum" select="number($sum) + number($elements[$pos + 1]/@age)"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> 

displays on your sample an input of the following results:

 20 60 90 170 

Template (original with inscriptions, etc.):

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" indent="yes" /> <xsl:variable name="txtlabels" select="tokenize('first,second,third,fourth,fifth,sixth,seventh,eights,ninth,tenth,eleventh,twelveth,thirteenth,fourteenth,fifteenth', ',')"/> <!-- Util template to generate labels --> <xsl:template name="getlabel"> <xsl:param name="startat" select="1"/> <xsl:param name="idx"/> <xsl:if test="number($startat) lt number($idx)"> <xsl:value-of select="$txtlabels[$startat]"/> <xsl:text> </xsl:text> <xsl:call-template name="getlabel"> <xsl:with-param name="startat" select="$startat + 1"/> <xsl:with-param name="idx" select="$idx"/> </xsl:call-template> </xsl:if> </xsl:template> <!-- Main Entry point --> <xsl:template match="/"> <xsl:variable name="count"> <xsl:value-of select="count(/people/man)"/> </xsl:variable> <xsl:call-template name="addthem"> <xsl:with-param name="count" select="count(/people/man)"/> <xsl:with-param name="pos" select="1"/> <xsl:with-param name="sum" select="/people/man[1]/@age"/> <xsl:with-param name="elements" select="/people/man"/> </xsl:call-template> </xsl:template> <!-- recursive calling template to sum up the ages --> <xsl:template name="addthem"> <xsl:param name="count"/> <xsl:param name="pos"/> <xsl:param name="sum"/> <xsl:param name="elements"/> <!-- get the label prefix, without the 'and' clause --> <xsl:variable name="thelabelprefix"> <xsl:call-template name="getlabel"> <xsl:with-param name="startat" select="1"/> <xsl:with-param name="idx" select="$pos"/> </xsl:call-template> </xsl:variable> <!-- Now append the 'and' clause, if required, to the labels!!! --> <xsl:variable name="thelabel"> <xsl:choose> <xsl:when test="number($pos) eq 1"> <xsl:value-of select="$txtlabels[$pos]"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat($thelabelprefix, ' and ', $txtlabels[$pos])"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="$thelabel"/> <xsl:text> : </xsl:text> <xsl:value-of select="$sum"/> <xsl:text> </xsl:text> <!-- recursive call to sum up the ages --> <xsl:if test="$pos lt number($count)"> <xsl:call-template name="addthem"> <xsl:with-param name="count" select="$count"/> <xsl:with-param name="pos" select="$pos + 1"/> <xsl:with-param name="sum" select="number($sum) + number($elements[$pos + 1]/@age)"/> <xsl:with-param name="elements" select="$elements"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> 

outputs the following result for your xml input:

 first : 20 first and second : 60 first second and third : 90 first second third and fourth : 170 

I have added comments inside, let me know if you need more help. It mainly uses two recursive patterns, one for β€œlabels” and the other for adding.

And, your sample output should read 90 and 170 instead of 110 and 190, or your input sample should indicate age = 50 instead of age = 30

+6
source

The following short stylesheet displays exactly the result that you first requested, including serial numbers:

Styles:

 <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:output method="text"/> <xsl:template match="/" > <xsl:for-each select="people/man"> <xsl:for-each select=".|preceding-sibling::man"> <xsl:value-of select="if (position() = last() and last() != 1) then ' and ' else ' '"/> <xsl:number format="w" ordinal="yes"/> </xsl:for-each> <xsl:text> age </xsl:text> <xsl:if test="position() > 1">(combined)</xsl:if> <xsl:value-of select="':', sum((.|preceding-sibling::man)/@age), '&#xa;'"/> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

Output:

  first age : 20 first and second age (combined): 60 first second and third age (combined): 90 first second third and fourth age (combined): 170 
+3
source

A simple non-recursive solution suitable for incremental sums of a small sequence of sibling elements :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="man"> <xsl:value-of select= "sum(@age|preceding-sibling::man/@age)"/> <xsl:text>&#xA;</xsl:text> </xsl:template> </xsl:stylesheet> 

When applied to the provided XML document (fixed so that it is executed correctly):

 <people> <man age="20" /> <man age="40" /> <man age="30" /> <man age="80" /> </people> 

required, the correct result is obtained :

 20 60 90 170 

II. An easy and effective solution for huge sequences (node-sets) is as follows: scanl template / function from FXSL :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://fxsl.sf.net/" xmlns:myAdd="f:myAdd" xmlns:myParam="f:myParam" > <xsl:import href="scanl.xsl"/> <xsl:output omit-xml-declaration="yes" indent="yes"/> <myAdd:myAdd/> <myParam:myParam>0</myParam:myParam> <xsl:template match="/"> <xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/> <xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/> <xsl:call-template name="scanl"> <xsl:with-param name="pFun" select="$vFun"/> <xsl:with-param name="pQ0" select="$vZero" /> <xsl:with-param name="pList" select="/*/*/@age"/> </xsl:call-template> </xsl:template> <xsl:template match="myAdd:*" mode="f:FXSL"> <xsl:param name="pArg1" select="0"/> <xsl:param name="pArg2" select="0"/> <xsl:value-of select="$pArg1 + $pArg2"/> </xsl:template> </xsl:stylesheet> 

This simple solution (just calling the template - without a recursive code to write) produces the desired result :

 <el>0</el> <el>20</el> <el>60</el> <el>90</el> <el>170</el> 
+1
source

All Articles