2015-09-21 143 views
1

我想让我的头绕过XSL递归。我需要使用带有单元标签的XSLT构建基于XML的HTML表格,其中位置映射到名为格式为“row.column”的名称的字符串属性。基于变量的XSL递归遍历

我想递归地从最后一个单元格开始计数回到第一个单元格(0.0)并开始构建表格。原因是有些单元可能跨越几个单元位置。因此,为了在HTML中使用,我不能只在for-each循环中处理单元格。最后一个单元格由表格属性标识; BodyRowCount和ColumnCount。

的XML看起来像这样

<Table HeaderRowCount="0" FooterRowCount="0" BodyRowCount="4" ColumnCount="2" > 
 
\t <Cell Self="ucd8iceei0" Name="0:0" RowSpan="1" ColumnSpan="1"> 
 
\t \t <Content>1</Content> 
 
\t </Cell> 
 
\t <Cell Self="ucd8iceei1" Name="1:0" RowSpan="1" ColumnSpan="1"> 
 
\t \t <Content>2</Content> 
 
\t </Cell> 
 
\t <Cell Self="ucd8iceei2" Name="2:0" RowSpan="1" ColumnSpan="2> 
 
\t \t <Content>3</Content> 
 
\t </Cell> 
 
\t <Cell Self="ucd8iceei3" Name="3:0" RowSpan="1" ColumnSpan="1"> 
 
\t \t <Content>4</Content> 
 
\t </Cell> 
 
\t <Cell Self="ucd8iceei4" Name="0:1" RowSpan="1" ColumnSpan="1"> 
 
\t \t <Content>5</Content> 
 
\t </Cell> 
 
\t <Cell Self="ucd8iceei6" Name="2:1" RowSpan="1" ColumnSpan="1"> 
 
\t \t <Content>7</Content> 
 
\t </Cell> 
 
\t <Cell Self="ucd8iceei7" Name="3:1" RowSpan="1" ColumnSpan="1"> 
 
\t \t <Content>8</Content> 
 
\t </Cell> 
 
</Table>

结果应该是这样的

<Table> 
 
\t <tr> 
 
\t \t <td>1</td> \t <td>5</td> 
 
\t </tr> 
 
\t <tr> 
 
\t \t <td> colspan="2">2</td> 
 
\t </tr> 
 
\t <tr> 
 
\t \t <td>3</td> <td>7</td> 
 
\t </tr> 
 
\t <tr> 
 
\t \t <td>4</td> \t <td>8</td> 
 
\t </tr> 
 
</Table>

伪形式的XLS在下面。我尝试在名为4.2的Cell中传递参数以开始这种情况,但似乎是不合法的,不能通过基于属性的参数,但是这肯定是可能的?

<xsl:template match="Table"> \t 
 
\t <table> 
 
\t \t <xsl:apply-templates select="Cell[@Name = concat(@BodyRowCount,':',ColumnCount)']"/> 
 
\t </table> 
 
</xsl:template> 
 

 
<xsl:template name="Cell[@name='0.0"> 
 
Found first cell, start first row in table 
 
<tr><td colspan=<xsl:value-of select=(concat(3&quot,@ColumnSpan,3&quot)) rowspan=<xsl:value-of select=(concat(3&quot,@rowSpan,3&quot))> Row content here </td></tr> 
 
</xsl:template> 
 

 
<xsl:template name="Cell[@name='0.*"> 
 
\t Call template with 'ROW-1, COLUMN back to max 
 
\t <xsl:apply-templates select="Cell[@Name = 'ROW-1:MAXCOLUMN']"> 
 
\t new row goes here 
 
\t <tr><td colspan=<xsl:value-of select=(concat(3&quot,@ColumnSpan,3&quot)) rowspan=<xsl:value-of select=(concat(3&quot,@rowSpan,3&quot))> Row content here </td></tr> 
 
</xsl:template> 
 

 
<xsl:template name="Cell[@name='*.*"> 
 
\t Call template with one column before 
 
\t <xsl:apply-templates select="Cell[@Name = SAME ROW:COLUMN-1']"> 
 
\t <td colspan=<xsl:value-of select=(concat(3&quot,@ColumnSpan,3&quot)) rowspan=<xsl:value-of select=(concat(3&quot,@rowSpan,3&quot))> Row content here </td> 
 
</xsl:template>

我寻找关于如何启动的递归循环中4.2回到0.0,通过遍历所有的位置,如果一个存在插入单元格的值建表一些帮助?

+1

您在使用XSLT 1.0和XSLT 2.0? –

+0

嗨我使用V2,但如果我更改为V1,那么Andreas提供的示例没有错误地工作。所以看起来版本的行为有所不同。 – Christian

+0

如果您使用XSLT 2.0,那么您可以使用内置的分组功能。 - 你能解释一下什么决定了这个colspan?在你的例子中,每个单元格都有RowSpan和ColumnSpan属性 - 但是你的输出具有不同的值。这是为什么? –

回答

0

你不需要为此递归。 xslt(1.0)的方式可能如下所示:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="rows" match="Cell" use="substring(@Name,1,1)"/> 
    <xsl:template match="Table"> 
     <table border="1"> 
      <xsl:apply-templates select="Cell[generate-id() = generate-id(key('rows',substring(@Name,1,1)))]" mode="row"> 
       <xsl:sort select="@Name"/> 
      </xsl:apply-templates> 
     </table> 
    </xsl:template> 
    <xsl:template match="Cell" mode="row"> 
     <xsl:variable name="row" select="substring(@Name,1,1)"/> 
     <tr> 
      <td> 
       <xsl:if test="not(following-sibling::Cell[substring(@Name,1,1) = $row])"> 
        <xsl:attribute name="colspan">2</xsl:attribute> 
       </xsl:if> 
       <xsl:value-of select="."/> 
      </td> 
      <xsl:apply-templates select="following-sibling::Cell[substring(@Name,1,1) = $row]" mode="secondcell"/> 
     </tr> 
    </xsl:template> 
    <xsl:template match="Cell" mode="secondcell"> 
     <td> 
      <xsl:value-of select="."/> 
     </td> 
    </xsl:template> 
</xsl:stylesheet> 

它是如何完成的?
- 建立在各行信息的关键(Name属性的第一个字符)
- 对发现的干细胞应用的模板行
- 保存名称属性的第一个字符变量
- 检查与相同的,如果下面的兄弟姐妹第一个字符存在
- 如果不加合并单元格(不需要从XML信息,但也可使用)
- 如果是这样,第二个单元格应用模板

相反<的xsl:value-of的选择=” 。“/>你可以使用< xsl:apply-templates />。这对于初学者来说不是那么明显,但更直接的xslt。纯粹主义者会要求它。

编辑@Christian:该键使用属性“name”的第一个字符,因此它为找到的每个第一个字符返回一个“单元”元素。这是每行一个,所以你可以找出行,而不需要在xml树中显式地显示这些信息。如果以下单元格具有相同的第一个字符,则会检测该行中的其他单元格。

编辑@Christian:在XSLT 2.0解决方案是越来越短,不需要一个关键:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> 
    <xsl:template match="Table"> 
     <table border="1"> 
      <xsl:for-each-group select="Cell" group-by="substring(@Name,1,1)"> 
       <tr> 
        <xsl:for-each select="current-group()"> 
         <td> 
          <xsl:if test="count(current-group())=1"> 
           <xsl:attribute name="colspan">2</xsl:attribute> 
          </xsl:if> 
          <xsl:value-of select="."/> 
         </td> 
        </xsl:for-each> 
       </tr> 
      </xsl:for-each-group> 
     </table> 
    </xsl:template> 
</xsl:stylesheet> 
+0

嗨,谢谢。这是完全不同的方法。我必须阅读'关键'表达。但现在看来,它似乎预计行:只允许返回最大一个键,并且返回2或更多,因为更多单元以相同的rownumber开头? – Christian

+0

@Christian:我添加了一个无钥匙的xslt 2.0解决方案 – Andreas

+0

这看起来不正确。你认为有两列;这可能会改变。此外,'substring(@ Name,1,1)'假定少于10行 - 这也可能改变。 –