如何统计评估XML节点,使用XSLT生成带正确rowspan值的HTML表格
问题描述
我有一个XML文档,节点层级如下:
- Management:包含若干Upravlinnya节点
- Upravlinnya:包含若干viddil(部门)节点
- viddil:包含多名员工节点
目前实现的逻辑仅对viddil层级的rowspan计算生效,其余层级的rowspan计算不正确。
现有代码
XSLT代码
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <body> <h2>Labka -7 Kondera Sviatoslav</h2> <table border="1"> <tr bgcolor="#9acd32"> <th>Management Name</th> <th>Upravlinnya Name</th> <th>Viddil Name</th> <th>Name</th> <th>Surname</th> <th>Birth Year</th> </tr> <xsl:for-each select="Managements/Management"> <xsl:for-each select="Upravlinnyas/Upravlinnya"> <xsl:for-each select="Viddils/Viddil"> <xsl:for-each select="Workers/Worker"> <tr> <xsl:if test="position() = 1"> <td rowspan="{last()}"> <xsl:value-of select="ancestor::Management[1]/ManagementName"/> </td> </xsl:if> <xsl:if test="position() = 1"> <td rowspan="{last()}"> <xsl:value-of select="ancestor::Upravlinnya[1]/UpravlinnyaName"/> </td> </xsl:if> <xsl:if test="position() = 1"> <td rowspan="{last()}"> <xsl:value-of select="ancestor::Viddil[1]/ViddilName"/> </td> </xsl:if> <td><xsl:value-of select="WorkerName"/></td> <td><xsl:value-of select="WorkerSurname"/></td> <td><xsl:value-of select="BirthYear"/></td> </tr> </xsl:for-each> </xsl:for-each> </xsl:for-each> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
XML示例
<?xml-stylesheet type = "text/xsl" href = "labka.xsl"?> <Managements> <Management id="1111"> <ManagementName>Management_1</ManagementName> <Upravlinnyas> <Upravlinnya id="1111"> <UpravlinnyaName>Upravlinnya_1</UpravlinnyaName> <Viddils> <Viddil id ="1111"> <ViddilName>Viddil_1</ViddilName> <Workers> <Worker id="1111"> <WorkerName>Sviatoslav</WorkerName> <WorkerSurname>Kondera</WorkerSurname> <BirthYear>2002</BirthYear> </Worker> <Worker id="2222"> <WorkerName>Orest</WorkerName> <WorkerSurname>Rodcevich</WorkerSurname> <BirthYear>1999</BirthYear> </Worker> <Worker id="3333"> <WorkerName>Olena</WorkerName> <WorkerSurname>Zelenska</WorkerSurname> <BirthYear>1978</BirthYear> </Worker> </Workers> </Viddil> <Viddil id ="7777"> <ViddilName>Viddil_17</ViddilName> <Workers> <Worker id="1111"> <WorkerName>SviatoslavNew</WorkerName> <WorkerSurname>KonderaNew</WorkerSurname> <BirthYear>2012</BirthYear> </Worker> </Workers> </Viddil> </Viddils> </Upravlinnya> </Upravlinnyas> </Management> <Management id="2222"> <ManagementName>Management_2</ManagementName> <Upravlinnyas> <Upravlinnya id="2222"> <UpravlinnyaName>Upravlinnya_2</UpravlinnyaName> <Viddils> <Viddil id ="2222"> <ViddilName>Viddil_2</ViddilName> <Workers> <Worker id="4444"> <WorkerName>Nidia</WorkerName> <WorkerSurname>Dorofeeva</WorkerSurname> <BirthYear>2016</BirthYear> </Worker> <Worker id="5555"> <WorkerName>Petro</WorkerName> <WorkerSurname>Poroshenko</WorkerSurname> <BirthYear>1976</BirthYear> </Worker> </Workers> </Viddil> </Viddils> </Upravlinnya> </Upravlinnyas> </Management> <Management id="3333"> <ManagementName>Management_333</ManagementName> <Upravlinnyas> <Upravlinnya id="3333"> <UpravlinnyaName>Upravlinnya_222</UpravlinnyaName> <Viddils> <Viddil id ="3333"> <ViddilName>Viddil_222</ViddilName> <Workers> <Worker id="6666"> <WorkerName>Marsha</WorkerName> <WorkerSurname>May</WorkerSurname> <BirthYear>1995</BirthYear> </Worker> </Workers> </Viddil> <Viddil id ="9999"> <ViddilName>Viddil_212</ViddilName> <Workers> <Worker id="1010"> <WorkerName>MarshaNew</WorkerName> <WorkerSurname>MayNew</WorkerSurname> <BirthYear>1905</BirthYear> </Worker> </Workers> </Viddil> </Viddils> </Upravlinnya> </Upravlinnyas> </Management> </Managements>
问题原因
原有代码中Management和Upravlinnya的rowspan取值用了last(),这个last()对应的是当前viddil下的worker总数,而不是整个Management/Upravlinnya下所有worker的总数。同时判断是否输出上层单元格的条件仅校验了当前worker是所属viddil的第一个员工,不符合上层单元格仅需在所属层级首个员工行输出一次的要求。
修正后的XSLT代码
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <body> <h2>Labka -7 Kondera Sviatoslav</h2> <table border="1"> <tr bgcolor="#9acd32"> <th>Management Name</th> <th>Upravlinnya Name</th> <th>Viddil Name</th> <th>Name</th> <th>Surname</th> <th>Birth Year</th> </tr> <xsl:for-each select="Managements/Management"> <!-- 计算当前Management下所有员工总数 --> <xsl:variable name="mgmtWorkerCount" select="count(.//Worker)"/> <xsl:for-each select="Upravlinnyas/Upravlinnya"> <!-- 计算当前Upravlinnya下所有员工总数 --> <xsl:variable name="upravWorkerCount" select="count(.//Worker)"/> <xsl:for-each select="Viddils/Viddil"> <xsl:variable name="viddilWorkerCount" select="count(Workers/Worker)"/> <xsl:for-each select="Workers/Worker"> <tr> <!-- 仅当前Management的第一个员工行输出Management单元格 --> <xsl:if test="position() = 1 and not(../preceding-sibling::Viddil) and not(../../preceding-sibling::Upravlinnya)"> <td rowspan="{$mgmtWorkerCount}"> <xsl:value-of select="ancestor::Management[1]/ManagementName"/> </td> </xsl:if> <!-- 仅当前Upravlinnya的第一个员工行输出Upravlinnya单元格 --> <xsl:if test="position() = 1 and not(../preceding-sibling::Viddil)"> <td rowspan="{$upravWorkerCount}"> <xsl:value-of select="ancestor::Upravlinnya[1]/UpravlinnyaName"/> </td> </xsl:if> <!-- 仅当前Viddil的第一个员工行输出Viddil单元格 --> <xsl:if test="position() = 1"> <td rowspan="{$viddilWorkerCount}"> <xsl:value-of select="ancestor::Viddil[1]/ViddilName"/> </td> </xsl:if> <td><xsl:value-of select="WorkerName"/></td> <td><xsl:value-of select="WorkerSurname"/></td> <td><xsl:value-of select="BirthYear"/></td> </tr> </xsl:for-each> </xsl:for-each> </xsl:for-each> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
修正说明
- 提前计算每个层级(Management/Upravlinnya/Viddil)下的所有员工总数,作为对应单元格的rowspan值
- 调整上层单元格的输出判断条件,确保每个层级的单元格只在该层级下第一个员工的行里输出一次,跨对应数量的行展示
内容的提问来源于stack exchange,提问作者Sviatoslavch
相关产品推荐
相关产品推荐

