XSLT可变长度子节点
问题描述:
我想写以下XML结构的XSLT模板:XSLT可变长度子节点
<dealership>
<division>
<division_name>BMW</division_name>
<models>
<model_no>328i</model_no>
<model_no>M3</model_no>
<model_no>X5</model_no>
<model_no>528i</model_no>
</models>
<salesman>
<salesman_name>Bob</salesman_name>
<salesman_name>Jerry</salesman_name>
</salesman>
<mechanics>
<mechanic_name>Greg</mechanic_name>
<mechanic_name>Mike</mechanic_name>
<mechanic_name>Sean</mechanic_name>
</mechanics>
</division>
</dealership>
我需要将其输出到HTML表格的格式如下:
<table>
<tr>
<th>Division</th>
<th>Models</th>
<th>Salesman</th>
<th>Mechanics</th>
</tr>
<tr>
<td>BMW</td>
<td>328i</td>
<td>Bob</td>
<td>Greg</td>
</tr>
<tr>
<td></td>
<td>M3</td>
<td>Jerry</td>
<td>Mike</td>
</tr>
<tr>
<td></td>
<td>X5</td>
<td></td>
<td>Sean</td>
</tr>
<tr>
<td></td>
<td>528i</td>
<td></td>
<td></td>
</tr>
</table>
问题是可以有任何数量的模型,推销员和机械师。所以不知何故,我需要得到大多数孩子的节点来知道表中要创建多少行,然后我需要一种方法来跟踪行中哪些单元格是空的。任何帮助将不胜感激。
答
我能想到的最好办法就是调用一个命名模板,将行号作为参数。然后,模板将检查是否有任何数据要输出,如果是,则建立该表的该行并使用下一个行号作为新参数值自行调用。
此代码演示。输出与您显示的HTML不同,因为您的HTML不符合您提供的XML数据。据我所知这是正确的。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="html" />
<xsl:template match="/dealership/division">
<table>
<tr>
<th>Division</th>
<th>Models</th>
<th>Salesman</th>
<th>Mechanics</th>
</tr>
<xsl:call-template name="row" >
<xsl:with-param name="i" select="1" />
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="row">
<xsl:param name="i"/>
<xsl:if test="models/model_no[$i] |
salesman/salesman_name[$i] |
mechanics/mechanic_name[$i]">
<tr>
<td>
<xsl:value-of select="division_name[$i]"/>
</td>
<td>
<xsl:value-of select="models/model_no[$i]"/>
</td>
<td>
<xsl:value-of select="salesman/salesman_name[$i]"/>
</td>
<td>
<xsl:value-of select="mechanics/mechanic_name[$i]"/>
</td>
</tr>
<xsl:call-template name="row" >
<xsl:with-param name="i" select="$i + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
输出
<table>
<tr>
<th>Division</th>
<th>Models</th>
<th>Salesman</th>
<th>Mechanics</th>
</tr>
<tr>
<td>BMW</td>
<td>328i</td>
<td>Bob</td>
<td>Greg</td>
</tr>
<tr>
<td></td>
<td>M3</td>
<td>Jerry</td>
<td>Mike</td>
</tr>
<tr>
<td></td>
<td>X5</td>
<td></td>
<td>Sean</td>
</tr>
</table>
+0
真棒!这工作得很好,非常感谢 – 2013-04-27 13:49:15
答
听到另一个可能的解决方案。
<xsl:template match="/dealership/division">
<xsl:variable name="maxcnt">
<xsl:for-each select="*" >
<xsl:sort select="count(*)" order="descending"/>
<xsl:if test ="position()=1">
<xsl:value-of select="name(.)"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<table>
<tr>
<th>Division</th>
<th>Models</th>
<th>Salesman</th>
<th>Mechanics</th>
</tr>
<xsl:apply-templates select="*[name()=$maxcnt]/*" mode="row"/>
</table>
</xsl:template>
<xsl:template match="*" mode="row">
<xsl:variable name="pos" select="count(preceding-sibling::*)+1"/>
<tr>
<td>
<xsl:value-of select="../../division_name[$pos]"/>
</td>
<td>
<xsl:value-of select="../../models/model_no[$pos]"/>
</td>
<td>
<xsl:value-of select="../../salesman/salesman_name[$pos]"/>
</td>
<td>
<xsl:value-of select="../../mechanics/mechanic_name[$pos]"/>
</td>
</tr>
</xsl:template>
的第一步是找出哪些部门的孩子通过它自身有孩子的最大计数。这是通过一个for-each来完成的,每个孩子都按照孩子的数量排序。因此,第一个是大多数孩子的。
<xsl:for-each select="*" >
<xsl:sort select="count(*)" order="descending"/>
<xsl:if test ="position()=1">
<xsl:value-of select="name(.)"/>
</xsl:if>
</xsl:for-each>
这是基于Dimitre Novatchev (*)
你输出HTML很好的解释并不完全符合您的输入。例如,“335i”和“528i”来自何处,而可怜的旧“M3”发生了什么? – 2013-04-27 08:32:06
你的权利,我做了编辑,让他们现在匹配 – 2013-04-27 13:38:19