apply-template 可以与 Muenchian 分组一起使用而不是 for-each 吗?
Can apply-template be used w/ Muenchian grouping instead of for-each?
据我了解,在不必要时使用 for-each 循环是一种不好的形式。有人可以告诉我如何将此 XSL 中带有分组的嵌套 for 循环转换为单独的模板吗?当 XML 是分层的时,这似乎很容易,但对于平面 XML,我还没有弄清楚执行此操作所需的 XPATH 表达式或其他语法。
示例 XML 数据:
<?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 样式表:
<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>
示例输出,顺序为:
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.
这应该是一个简单的任务,因为无论您使用 xsl:for-each
还是 xsl:apply-templates
,xpath 表达式都是相同的。唯一要考虑的是,至少在您的示例中,您最终可能会得到多个匹配同一元素的模板(在本例中为 Employee
)。但是,您可以使用 mode
属性
来解决这个问题
考虑这个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>
用单个 xsl:apply-templates
替换它,像这样
<xsl:apply-templates select="Employee[count(. | key('group-by-person', PERSON_ID)[1]) = 1]" mode="person" />
然后,将内部代码从 for-each 移动到模板匹配中
<xsl:template match="Employee" mode="person">
<!-- Inner code -->
</xsl:template>
您可以以完全相同的方式对嵌套 xsl:for-each
重复这些步骤。
试试这个 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>
使用xsl:for-each
在很多情况下不应该真正被视为"bad form"。要记住的是 xsl:for-each
是一个映射结构,而不是一个循环。 (XSLT 处理器可以自由地并行处理选定的节点)。在您的情况下,xsl:for-each
的使用导致了过多的嵌套,这会使代码更难阅读,但除此之外,它并没有真正的问题。
据我了解,在不必要时使用 for-each 循环是一种不好的形式。有人可以告诉我如何将此 XSL 中带有分组的嵌套 for 循环转换为单独的模板吗?当 XML 是分层的时,这似乎很容易,但对于平面 XML,我还没有弄清楚执行此操作所需的 XPATH 表达式或其他语法。
示例 XML 数据:
<?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 样式表:
<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>
示例输出,顺序为:
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.
这应该是一个简单的任务,因为无论您使用 xsl:for-each
还是 xsl:apply-templates
,xpath 表达式都是相同的。唯一要考虑的是,至少在您的示例中,您最终可能会得到多个匹配同一元素的模板(在本例中为 Employee
)。但是,您可以使用 mode
属性
考虑这个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>
用单个 xsl:apply-templates
替换它,像这样
<xsl:apply-templates select="Employee[count(. | key('group-by-person', PERSON_ID)[1]) = 1]" mode="person" />
然后,将内部代码从 for-each 移动到模板匹配中
<xsl:template match="Employee" mode="person">
<!-- Inner code -->
</xsl:template>
您可以以完全相同的方式对嵌套 xsl:for-each
重复这些步骤。
试试这个 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>
使用xsl:for-each
在很多情况下不应该真正被视为"bad form"。要记住的是 xsl:for-each
是一个映射结构,而不是一个循环。 (XSLT 处理器可以自由地并行处理选定的节点)。在您的情况下,xsl:for-each
的使用导致了过多的嵌套,这会使代码更难阅读,但除此之外,它并没有真正的问题。