xml - Can apply-template be used w/ Muenchian grouping instead of for-each? -


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