xslt:我如何使用xslt创建一个包含多列和多行的表?

时间:2009-04-20 15:01:31

标签: xml xslt

如何使用此xml并为每个“section”元素创建一个包含列的表,然后使用xslt显示该列中的所有“document”元素?

<Documents>
  <Section>
  <SectionName>Green</SectionName>
    <Document>
      <FileName>Tier 1 Schedules</FileName>     
    </Document>
    <Document>
      <FileName>Tier 3 Schedules</FileName>      
    </Document>
    <Document>
      <FileName>Setback Schedule</FileName>    
    </Document>
    <Document>
      <FileName>Tier 2 Governance</FileName>    
    </Document>
 </Section>
 <Section>
 <SectionName>MRO/Refurb</SectionName>
   <Document>
     <FileName>Tier 2 Governance</FileName>    
   </Document>
 </Section>

谢谢, 人

2 个答案:

答案 0 :(得分:3)

此解决方案不使用递归,并使用一些有用的XSLT技术,例如Muenchian分组,键,查找最大值和迭代而不递归。

此转化

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:key name="kSectsByValue" match="SectionName"
         use="."/>

    <xsl:key name="kDocBySect" match="Document"
         use="../SectionName"/>

    <xsl:variable name="vCols" select=
       "/*/*/SectionName
                [generate-id()
                =          
                 generate-id(key('kSectsByValue',.)[1])
                 ]"/>

    <xsl:variable name="vMaxRows">
             <xsl:for-each select="$vCols">
               <xsl:sort data-type="number" order="descending"
                    select="count(key('kDocBySect', .))"      />
               <xsl:if test="position() = 1">
                 <xsl:value-of select="count(key('kDocBySect', .))"/>
               </xsl:if>
             </xsl:for-each>
    </xsl:variable>

    <xsl:template match="/">
             <table>
               <tr>
                 <xsl:apply-templates select="$vCols"/>
               </tr>

               <xsl:for-each select=
                 "(/*/*/Document)[not(position() > $vMaxRows)]">                   
                 <tr>

                   <xsl:variable name="vPos" select="position()"/>

                   <xsl:for-each select="$vCols">
                     <td>
                       <xsl:value-of select=
                           "../Document[$vPos]/FileName"/>
                     </td>
                   </xsl:for-each>

                 </tr>
              </xsl:for-each>
            </table>

    </xsl:template>

    <xsl:template match="SectionName">
            <td>
              <xsl:value-of select="." />
            </td>   
    </xsl:template>
</xsl:stylesheet>

应用于原始XML文档(更正为格式良好):

<Documents>
    <Section>
        <SectionName>Green</SectionName>
        <Document>
            <FileName>Tier 1 Schedules</FileName>
        </Document>
        <Document>
            <FileName>Tier 3 Schedules</FileName>
        </Document>
        <Document>
            <FileName>Setback Schedule</FileName>
        </Document>
        <Document>
            <FileName>Tier 2 Governance</FileName>
        </Document>
    </Section>
    <Section>
        <SectionName>MRO/Refurb</SectionName>
        <Document>
            <FileName>Tier 2 Governance</FileName>
        </Document>
    </Section>
</Documents>

产生所需的结果

<table>
   <tr>
      <td>Green</td>
      <td>MRO/Refurb</td>
   </tr>
   <tr>
      <td>Tier 1 Schedules</td>
      <td>Tier 2 Governance</td>
   </tr>
   <tr>
      <td>Tier 3 Schedules</td>
      <td/>
   </tr>
   <tr>
      <td>Setback Schedule</td>
      <td/>
   </tr>
   <tr>
      <td>Tier 2 Governance</td>
      <td/>
   </tr>
</table>

请注意

  1. 我们使用Muenchian method for grouping 来查找所有不同的列名,而不是依赖于XML文档中的唯一列名称。

  2. Keys 用于Muenchian分组和查找属于某列的所有项目。

  3. 找到最大行数并保存在变量$vMaxRows

  4. 我们迭代N次以生成表格的N行 - 不使用递归

  5. 通过将模板应用于列中位置为N的所有列项来输出N行。

答案 1 :(得分:1)

这是一种可能的解决方案:

<xsl:variable name="vCountRows">
  <xsl:apply-templates select="Documents/Section[1]" mode="findmax" />
</xsl:variable>

<xsl:variable name="vCountCols" select="count(Documents/Section)" />

<xsl:template match="/Documents">
  <table r="{$vCountRows}" s="{$vCountCols}">
    <thead>
      <xsl:call-template name="create-thead" />
    </thead>
    <tbody>
      <xsl:call-template name="create-tr" />
    </tbody>
  </table>
</xsl:template>

<xsl:template name="create-thead">
   <tr>
    <xsl:apply-templates select="Section" />
  </tr>    
</xsl:template>

<xsl:template match="Section">
  <th><xsl:value-of select="SectionName" /></th>
</xsl:template>

<xsl:template name="create-tr">
  <xsl:param name="row" select="1" />

  <tr>
    <xsl:call-template name="create-td">
      <xsl:with-param name="row" select="$row" />
    </xsl:call-template>
  </tr>

  <xsl:if test="$row &lt; $vCountRows">
    <xsl:call-template name="create-tr">
      <xsl:with-param name="row" select="$row + 1" />
    </xsl:call-template>
  </xsl:if>

</xsl:template>

<xsl:template name="create-td">
  <xsl:param name="col" select="1" />
  <xsl:param name="row" select="1" />

  <td>
    <xsl:value-of select="Section[$col]/Document[$row]/FileName" />
  </td>

  <xsl:if test="$col &lt; $vCountCols">
    <xsl:call-template name="create-td">
      <xsl:with-param name="col" select="$col + 1" />
      <xsl:with-param name="row" select="$row" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template match="Section" mode="findmax">
  <xsl:variable name="c" select="count(Document)" />
  <xsl:variable name="next" select="following-sibling::Section[count(Document) &gt; $c][1]" />

  <xsl:choose>    
    <xsl:when test="$next">
      <xsl:apply-templates select="$next" mode="findmax" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$c" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

根据您的输入,它会产生:

<table>
  <thead>
    <tr>
      <td>Green</td>
      <td>MRO/Refurb</td>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tier 1 Schedules</td>
      <td>Tier 2 Governance</td>
    </tr>
    <tr>
      <td>Tier 3 Schedules</td>
      <td></td>
    </tr>
    <tr>
      <td>Setback Schedule</td>
      <td></td>
    </tr>
    <tr>
      <td>Tier 2 Governance</td>
      <td></td>
    </tr>
  </tbody>
</table>

一般的apporach是这样的:

  1. 找出我们将获得多少行(这发生在<xsl:template match="Section" mode="findmax">中,它以递归方式查找具有最大<Document>个节点数的节
  2. 找出我们将获得多少列(通过计算<Section> s的数量)
  3. 调用创建<tr>的模板,并一直调用自己,直到创建了所有必要的行
  4. 在此模板中,调用第二个模板,此模板创建<td> s。它一直调用自己直到达到最大列数(从步骤2开始)
  5. 算法:

    • 使用递增的列号和行号作为索引
    • 使用递归来实现一个一个索引的增量(因为在XSLT中实际上无法增加变量)
    • 为文档较少的部分创建正确数量的空单元格

    存在一个更高效的版本(可能使用<xsl:key>),我将更多地考虑优化我的版本。