使用 XSLT 对 XML 进行分组






4.53/5 (12投票s)
2002年2月18日
2分钟阅读

221253

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
小时数的摘要。类似于这样
笨拙的方法
一种方法是循环遍历 <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;
}
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。