2013-04-27 71 views
1

我想写以下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> 

问题是可以有任何数量的模型,推销员和机械师。所以不知何故,我需要得到大多数孩子的节点来知道表中要创建多少行,然后我需要一种方法来跟踪行中哪些单元格是空的。任何帮助将不胜感激。

+0

你输出HTML很好的解释并不完全符合您的输入。例如,“335i”和“528i”来自何处,而可怜的旧“M3”发生了什么? – 2013-04-27 08:32:06

+0

你的权利,我做了编辑,让他们现在匹配 – 2013-04-27 13:38:19

回答

0

我能想到的最好办法就是调用一个命名模板,将行号作为参数。然后,模板将检查是否有任何数据要输出,如果是,则建立该表的该行并使用下一个行号作为新参数值自行调用。

此代码演示。输出与您显示的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

0

听到另一个可能的解决方案。

<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 (*)

+0

+1很好的解决方案。 – ABach 2013-04-27 19:09:39

+0

哦,谢谢。我已经开始害怕我是唯一一个喜欢它的人,至少有一点。 ;-)顺便说一句,北达科他州真的这么糟吗? – 2013-04-27 19:35:15

+0

负面 - 我曾经看过一部电影中的引用,并且发现它很有趣。 :) – ABach 2013-04-27 20:04:18