65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 XSLT 对 XML 进行分组

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.53/5 (12投票s)

2002年2月18日

2分钟阅读

viewsIcon

221253

downloadIcon

1685

使用 XSLT 根据 XML 中唯一的 ID 值对 XML 元素进行分组。XML 被转换为 HTML 表格。

引言

如果想要处理每个 XML 元素,使用 XSLT 处理 XML 元素列表非常简单。但是,如果想要对 XML 元素进行分组以显示摘要呢?考虑以下 XML

<?xml version="1.0" ?>
<Employees>
 <Employee>
  <TeamID>1</TeamID>
  <TeamName>Sales</TeamName>
  <TaskID>1</TaskID>
  <Hours>5</Hours>
  <EmployeeID>1</EmployeeID>
  <Name>Bob</Name>
  <Surname>Shibob</Surname>
 </Employee>
 <Employee>
  <TeamID>1</TeamID>
  <TeamName>Sales</TeamName>
  <TaskID>2</TaskID>
  <Hours>4</Hours>
  <EmployeeID>1</EmployeeID>
  <Name>Bob</Name>
  <Surname>Shibob</Surname>
 </Employee>
 <Employee>
  <TeamID>1</TeamID>
  <TeamName>Sales</TeamName>
  <TaskID>4</TaskID>
  <Hours>7</Hours>
  <EmployeeID>2</EmployeeID>
  <Name>Sara</Name>
  <Surname>Lee</Surname>
 </Employee>
 <Employee>
  <TeamID>2</TeamID>
  <TeamName>Finance</TeamName>
  <TaskID>5</TaskID>
  <Hours>2</Hours>
  <EmployeeID>3</EmployeeID>
  <Name>John</Name>
  <Surname>Smith</Surname>
 </Employee>
 <Employee>
  <TeamID>2</TeamID>
  <TeamName>Finance</TeamName>
  <TaskID>3</TaskID>
  <Hours>4</Hours>
  <EmployeeID>4</EmployeeID>
  <Name>Penny</Name>
  <Surname>Wise</Surname>
 </Employee>
 <Employee>
  <TeamID>2</TeamID>
  <TeamName>Finance</TeamName>
  <TaskID>5</TaskID>
  <Hours>3</Hours>
  <EmployeeID>4</EmployeeID>
  <Name>Penny</Name>
  <Surname>Wise</Surname>
 </Employee>
</Employees>

假设需要显示按 Team 分组的 Employee 小时数的摘要。类似于这样

Sample Image - groupxml.jpg

笨拙的方法

一种方法是循环遍历 <Employee> 元素列表,只有在 EmployeeID 更改时才显示一行。虽然这种方法可行,但效率低下且笨拙,因为在处理每个 <Employee> 时,都需要跟踪上一个 <Employee> 元素的 ID。这看起来不太美观。

高效的方法

更简洁、更高效的方法是构建一个唯一的键列表,然后使用这些键对结果进行分组。(这称为 *Muenchian 方法*。)

首先,必须定义用于分组 <Employee> 元素所需的键。需要一个用于 TeamID,另一个用于 EmployeeID

<xsl:key name="keyTeamID" match="Employee" use="TeamID" />
<xsl:key name="keyEmployeeID" match="Employee" use="EmployeeID" />

为每个唯一的 TeamID 选择每个元素组的第一个元素。

<xsl:for-each select="//Employee[generate-id(.) = generate-id(key('keyTeamID', TeamID)[1])]">

将属于该 Team 的所有 <Employee> 元素获取到一个变量中。

<!-- Save the ID of the Team to a variable -->
<xsl:variable name="lngTeamID"><xsl:value-of select="TeamID" /></xsl:variable>
<!-- Select all the Employees belonging to the Team -->
<xsl:variable name="lstEmployee" select="//Employee[TeamID=$lngTeamID]" />

此列表中的 <Employee> 元素现在必须按 EmployeeID 分组。这类似于按 TeamID 分组,只是在这种情况下,只需要选择包含在变量中的列表中的元素;不需要从整个结果集中选择元素。

<xsl:for-each select="$lstEmployee[generate-id(.) = 
generate-id(key('keyEmployeeID', EmployeeID)[1])]">

现在可以很容易地显示每个 Employee 的总 Hours

<xsl:value-of select="sum($lstEmployee[EmployeeID=$lngEmployeeID]/Hours)" />

完整的源代码

这是用于渲染图像中表格的整个 XSLT

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- Define keys used to group elements -->
<xsl:key name="keyTeamID" match="Employee" use="TeamID" />
<xsl:key name="keyEmployeeID" match="Employee" use="EmployeeID" />

<xsl:template match="/">
 <html>
  <head>
   <title>Employee Hours By Team</title>
   <link type="text/css" rel="stylesheet" href="groupxml.css" />
  </head>
  <body>
   <h3>Employee Hours By Team</h3>
   <table>
   
    <!-- Process each Team -->
    <xsl:for-each select="//Employee[generate-id(.) = generate-id(key('keyTeamID', TeamID)[1])]">
     <xsl:variable name="lngTeamID"><xsl:value-of select="TeamID" /></xsl:variable>
     <!-- Select all the Employees belonging to the Team -->
     <xsl:variable name="lstEmployee" select="//Employee[TeamID=$lngTeamID]" />
     <!-- Show details for Employees in Team -->
     <xsl:call-template name="ShowEmployeesInTeam">
      <xsl:with-param name="lstEmployee" select="$lstEmployee" />
     </xsl:call-template>
    </xsl:for-each>
   
    <tr>
     <td colspan="4" class="RightJustified DarkBack">Grand Total</td>
     <td colspan="1" class="RightJustified DarkBack">
      <!-- Show Grand Total of hours for all Employees -->
      <xsl:value-of select="sum(//Employee/Hours)" />
     </td>
    </tr>
   </table>
  </body>
 </html>
</xsl:template>

<xsl:template name="ShowEmployeesInTeam">
 <xsl:param name="lstEmployee" />
 
 <!-- Show the name of the Team currently being processed -->
 <tr>
  <td colspan="4" class="DarkBack">TEAM: 
  <xsl:value-of select="<code>$lstEmployee[1]/TeamName</code>" /></td>
  <td colspan="1" class="DarkBack RightJustified">HOURS</td>
 </tr>
 
 <!-- Show the total hours for each Employee in the Team -->
 <xsl:for-each select="$lstEmployee
 [generate-id(.) = generate-id(key('keyEmployeeID', EmployeeID)[1])]">
  <xsl:variable name="lngEmployeeID" select="EmployeeID" />
  <!-- Show details of each Employee -->
  <tr>
   <td colspan="4">
    <xsl:value-of select="$lstEmployee[EmployeeID=$lngEmployeeID]/Name" />
     <xsl:value-of select="$lstEmployee[EmployeeID=$lngEmployeeID]/Surname" />
   </td>
   <td colspan="1" class="RightJustified">
    <!-- Show the total hours for the current Employee -->
    <xsl:value-of select="sum($lstEmployee[EmployeeID=$lngEmployeeID]/Hours)" />
   </td>
  </tr>
 </xsl:for-each>
 
 <tr>
  <td colspan="4" class="LightBack RightJustified">Sub-Total</td>
  <td colspan="1" class="LightBack RightJustified">
   <!-- Show the total hours for all Employees in the Team -->
   <xsl:value-of select="sum($lstEmployee/Hours)" />
  </td>
 </tr>
</xsl:template>

</xsl:stylesheet>

用于渲染图像中表格的 CSS

table
{  border-collapse: collapse;
   width: 30%;
   table-layout: fixed;
   border-style: solid;
}
table, td
{  border-width: 1px;
}
td
{  color: black;
   font-family: Arial;
   font-size: x-small;
   border-right-style: none;
   border-left-style: none;
   border-top-style: solid;
   border-bottom-style: solid;
}
.DarkBack
{  background-color: #0066FF;
   background-color: blue;
   color: white;
   font-weight: bold;
}
.LightBack
{  background-color: #99CCFF;
   color: black;
}
.RightJustified
{  text-align: right;
}

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.