我的源xml看起来像:
<TABLE>
<ROW>
<CELL ROWSPAN="3"> Test </CELL>
<CELL ROWSPAN="2"> Test </CELL>
<CELL ROWSPAN="1"> Test </CELL>
<CELL ROWSPAN="3"> Test </CELL>
<CELL ROWSPAN="1"> Test </CELL>
</ROW>
<ROW>
<CELL ROWSPAN="1"> Test </CELL>
<CELL ROWSPAN="1"> Test </CELL>
</ROW>
</TABLE>
正确的转换输出应如下所示:
<tbody>
<row>
<entry colname="1"> Test </entry>
<entry colname="2"> Test </entry>
<entry colname="3"> Test </entry>
<entry colname="4"> Test </entry>
<entry colname="5"> Test </entry>
</row>
<row>
<entry colname="3"> Test </entry>
<entry colname="5"> Test </entry>
</row>
</tbody>
如您所见,棘手的部分是第二行元素。由于第一行有几个单元格占据多行,因此它将影响第二行的别名,这就是第二行第一项以“ 3”而不是“ 1”开头的原因。我不知道如何在这里画一张桌子,但是如果您在纸上画一张桌子,您会很容易理解。
目前,我有以下xsl可以部分捕获此内容(我省略了其他信息,因为我仅遇到@colname问题)
<xsl:template match="CELL">
<xsl:if test="../preceding-sibling::ROW[1]/CELL[1]/@ROWSPAN > 1">
<xsl:attribute name="colname" select="position()+count(../preceding-sibling::ROW[1]/CELL[@ROWSPAN>1])"/>
..
</xsl:if>
</xsl:template>
这将不能很好地工作,因为它将包括行数跨度较大的所有CELL,因此结果将如下所示:
<row>
<entry colname="4"> Test </entry>
<entry colname="5"> Test </entry>
</row>
虽然第一个条目实际上应该从3开始。
我发现很难描述这个问题,但是如果需要更多信息,我会尽力而为,请在下面留下评论。
最佳答案
这个解决方案非常复杂,我有点ing不安,可能会有更好的方法来做到这一点,但是似乎可行:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="TABLE">
<tbody>
<xsl:apply-templates select="ROW[1]" />
</tbody>
</xsl:template>
<xsl:template match="ROW">
<xsl:param name="occupiedcols" />
<row>
<xsl:apply-templates select="CELL[1]">
<xsl:with-param name="occupiedcols" select="$occupiedcols" />
</xsl:apply-templates>
</row>
<xsl:apply-templates select="following-sibling::ROW[1]">
<xsl:with-param name="occupiedcols">
<xsl:apply-templates select="CELL[1]" mode="getoccupied">
<xsl:with-param name="occupiedcols" select="$occupiedcols" />
</xsl:apply-templates>
<xsl:text></xsl:text>
</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="CELL">
<xsl:param name="occupiedcols" />
<xsl:param name="col" select="1" />
<xsl:variable name="thiscol" select="$col + string-length(substring-before(substring($occupiedcols,$col,255),'0'))" />
<xsl:element name="entry">
<xsl:attribute name="colname">
<xsl:value-of select="$thiscol" />
</xsl:attribute>
</xsl:element>
<xsl:apply-templates select="following-sibling::CELL[1]">
<xsl:with-param name="occupiedcols" select="$occupiedcols"/>
<xsl:with-param name="col" select="$thiscol + 1" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="CELL" mode="getoccupied">
<xsl:param name="occupiedcols" />
<xsl:param name="col" select="1" />
<xsl:variable name="thiscol" select="$col + string-length(substring-before(substring($occupiedcols,$col,255),'0'))" />
<xsl:choose>
<xsl:when test="contains(substring($occupiedcols,$col,255),'0')">
<xsl:value-of select="translate(substring-before(substring($occupiedcols,$col,255),'0'),'0123456789','-012345678')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(substring($occupiedcols,$col,255),'123456789','012345678')" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="@ROWSPAN - 1" />
<xsl:if test="not(following-sibling::CELL)">
<xsl:value-of select="translate(substring($occupiedcols,$thiscol + 1, 255),'0123456789','0012345678')" />
</xsl:if>
<xsl:apply-templates select="following-sibling::CELL[1]" mode="getoccupied">
<xsl:with-param name="occupiedcols" select="$occupiedcols"/>
<xsl:with-param name="col" select="$thiscol + 1" />
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
有一个已知的问题:如果一个单元格跨越9行以上,它将中断。如果存在问题,实际上很容易适应。
此外,它不支持
COLSPAN
的任何使用。它通过传递详细说明每一列仍具有单元格的行数的数字字符串来工作,因此在您的示例中第二行将传递“ 21020”,并根据0的位置计算
colname
属性。每行第二遍将数字减少一位,但每个单元格的ROWSPAN
用0代替。此解决方案还假定所有单元格都具有
ROWSPAN
属性,即使它们仅跨一个。如果不是这种情况,我可以添加一种支持默认值1
的方法。