as understand it, using for-each loops when unnecessary bad form. can show me how might convert nested for-loops grouping in xsl individual templates? seems easy when xml hierarchical, flat xml, have yet figure out xpath expression or other syntax needed this.
sample xml data:
<?xml version = "1.0"?> <?xml-stylesheet type = "text/xsl" href = "time_detail_employee_m.xsl"?> <employees> <employee> <company_id>83207</company_id> <prj_project_id>104</prj_project_id> <prj_project_name>portal</prj_project_name> <person_id>5881</person_id> <tm_first_name>dave</tm_first_name> <tm_last_name>morgan</tm_last_name> <sr_id>3075</sr_id> <sr_title>shoe page</sr_title> <tm_begin_dt>2015-12-11t00:00:00</tm_begin_dt> <tm_begin_time>10:45:00</tm_begin_time> <tm_end_time>16:30:00</tm_end_time> <tm_time_cd>reg</tm_time_cd> <tm_billable>f</tm_billable> <tm_week>50</tm_week> <tm_calculated_time>5.750000</tm_calculated_time> </employee> <employee> ... </employee> ... </employees>
xsl stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:key name="group-by-person" match="employee" use="person_id" /> <xsl:key name="group-by-week" match="employee" use="concat(person_id,'|',tm_week)" /> <xsl:key name="group-by-day" match="employee" use="concat(person_id,'|',tm_week,'|',tm_begin_dt)" /> <xsl:variable name="space"><xsl:text> </xsl:text></xsl:variable> <xsl:template match="/"> <html><body> <xsl:apply-templates /> </body></html> </xsl:template> <xsl:template match="employees"> <xsl:for-each select="employee[count(. | key('group-by-person', person_id)[1]) = 1]"> <xsl:sort select="tm_last_name" /> <p><xsl:value-of select="tm_first_name" /><xsl:value-of select="$space"/><xsl:value-of select="tm_last_name" /></p><br /> <!-- begin week grouping --> <xsl:for-each select="key('group-by-person', person_id)[count(. | key('group-by-week', concat(person_id,'|',tm_week))[1]) = 1]"> <xsl:sort select="tm_week" data-type="number"/> <p><xsl:value-of select="tm_week" /></p><br/> <!-- begin day grouping --> <xsl:for-each select="key('group-by-week', concat(person_id,'|',tm_week))[count(. | key('group-by-day', concat(person_id,'|',tm_week,'|',tm_begin_dt))[1]) = 1]"> <xsl:sort select="tm_begin_dt" /> <xsl:value-of select="substring-before(tm_begin_dt,'t')" /> <br/> <xsl:for-each select="key('group-by-day', concat(person_id,'|',tm_week,'|',tm_begin_dt))"> <p><xsl:value-of select="tm_begin_time" /><xsl:value-of select="$space"/><xsl:value-of select="tm_end_time" /></p><br/> </xsl:for-each> <br/><xsl:text>daily sum = </xsl:text> <xsl:value-of select="sum(key('group-by-day', concat(person_id,'|',tm_week,'|',tm_begin_dt))/tm_calculated_time)" /> <br/> </xsl:for-each> <!-- end day grouping --> <br/><xsl:text>weekly sum = </xsl:text> <xsl:value-of select="sum(key('group-by-week', concat(person_id,'|',tm_week))/tm_calculated_time)" /> <br/> </xsl:for-each> <!-- end week grouping --> <br/><xsl:text>person_id sum = </xsl:text> <xsl:value-of select="sum(key('group-by-person', person_id)/tm_calculated_time)" /> <br/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
sample output, on order of:
name 1 (based on person_id) week 1 (based on tm_week) monday (based on tm_begin_dt) time1 - time2 (tm_begin_time - tm_end_time) time3 - time4 tuesday time1 - time2 week 2 thursday time1 - time2 time3 - time4 time5 - time6 name 2 week 1 wednesday time1 - time2 name 3, etc.
it should straight-forward task, xpath expressions same whether use xsl:for-each
or xsl:apply-templates
. thing consider that, in example @ least, may end multiple templates matching same element (employee
in case). however, can around using mode
attribute
consider xsl:for-each
<xsl:for-each select="employee[count(. | key('group-by-person', person_id)[1]) = 1]"> <xsl:sort select="tm_last_name" /> <!-- inner code --> </xsl:for-each>
replace single xsl:apply-templates
, so
<xsl:apply-templates select="employee[count(. | key('group-by-person', person_id)[1]) = 1]" mode="person" />
then, move inner code for-each template match
<xsl:template match="employee" mode="person"> <!-- inner code --> </xsl:template>
you can repeat these steps nested xsl:for-each
in same manner.
try xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:key name="group-by-person" match="employee" use="person_id" /> <xsl:key name="group-by-week" match="employee" use="concat(person_id,'|',tm_week)" /> <xsl:key name="group-by-day" match="employee" use="concat(person_id,'|',tm_week,'|',tm_begin_dt)" /> <xsl:output method="html" indent="yes" /> <xsl:variable name="space"><xsl:text> </xsl:text></xsl:variable> <xsl:template match="/"> <html><body> <xsl:apply-templates /> </body></html> </xsl:template> <xsl:template match="employees"> <xsl:apply-templates select="employee[count(. | key('group-by-person', person_id)[1]) = 1]" mode="person"> <xsl:sort select="tm_last_name" /> </xsl:apply-templates> </xsl:template> <xsl:template match="employee" mode="person"> <p><xsl:value-of select="tm_first_name" /><xsl:value-of select="$space"/><xsl:value-of select="tm_last_name" /></p><br /> <!-- begin week grouping --> <xsl:apply-templates select="key('group-by-person', person_id)[count(. | key('group-by-week', concat(person_id,'|',tm_week))[1]) = 1]" mode="week"> <xsl:sort select="tm_week" data-type="number"/> </xsl:apply-templates> <!-- end week grouping --> <br/><xsl:text>person_id sum = </xsl:text> <xsl:value-of select="sum(key('group-by-person', person_id)/tm_calculated_time)" /> <br/> </xsl:template> <xsl:template match="employee" mode="week"> <p><xsl:value-of select="tm_week" /></p><br/> <!-- begin day grouping --> <xsl:apply-templates select="key('group-by-week', concat(person_id,'|',tm_week))[count(. | key('group-by-day', concat(person_id,'|',tm_week,'|',tm_begin_dt))[1]) = 1]" mode="day"> <xsl:sort select="tm_begin_dt" /> </xsl:apply-templates> <!-- end day grouping --> <br/><xsl:text>weekly sum = </xsl:text> <xsl:value-of select="sum(key('group-by-week', concat(person_id,'|',tm_week))/tm_calculated_time)" /> <br/> </xsl:template> <xsl:template match="employee" mode="day"> <xsl:value-of select="substring-before(tm_begin_dt,'t')" /> <br/> <xsl:for-each select="key('group-by-day', concat(person_id,'|',tm_week,'|',tm_begin_dt))"> <p><xsl:value-of select="tm_begin_time" /><xsl:value-of select="$space"/><xsl:value-of select="tm_end_time" /></p><br/> </xsl:for-each> <br/><xsl:text>daily sum = </xsl:text> <xsl:value-of select="sum(key('group-by-day', concat(person_id,'|',tm_week,'|',tm_begin_dt))/tm_calculated_time)" /> <br/> </xsl:template> </xsl:stylesheet>
using xsl:for-each
should not considered "bad form" in many cases. thing bear in mind xsl:for-each
mapping construct, , not loop. (the xslt processor free process selected nodes in parallel). in case, use of xsl:for-each
has led excessive nesting, can make code harder read, apart that, there nothing wrong it.
Comments
Post a Comment