Xml 如何正确指定表的colname?
我的源xml看起来像:Xml 如何正确指定表的colname?,xml,xslt,xpath,html-table,Xml,Xslt,Xpath,Html Table,我的源xml看起来像: <TABLE> <ROW> <CELL ROWSPAN="3"> Test </CELL> <CELL ROWSPAN="2"> Test </CELL> <CELL ROWSPAN="1"> Test </CELL> <CELL ROWSPAN="3"> Test </CELL> <CELL ROWSP
<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>
<row>
<entry colname="4"> Test </entry>
<entry colname="5"> Test </entry>
</row>
试验
试验
试验
试验
试验
试验
试验
如您所见,棘手的部分是第二行元素。由于第一行有多个单元格占据多行,因此会影响第二行的colname,这就是为什么第二行的第一个条目以colname“3”而不是“1”开头的原因。我不知道如何在这里画一张桌子,但如果你把它画在纸上,你会很容易理解的
目前,我有以下xsl可以部分地捕获这一点(我省略了其他信息,因为我只对@colname有问题)
..
这不会很有效,因为它会将具有多行跨度的所有单元格都包含在其计数中,因此结果如下所示:
<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>
<row>
<entry colname="4"> Test </entry>
<entry colname="5"> Test </entry>
</row>
试验
试验
而第一个条目实际上应该从3开始
我发现描述这个问题有点困难,但我会尽我最大的努力,如果需要更多信息,请在下面留下评论。这个解决方案相当复杂,我有一种唠叨的感觉,可能有更好的方法来解决,但它似乎有效:
<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
的方法。我认为一个好主意是为每列保留一个位向量,该位向量包含此列中前一个单元格将扩展到的行数。要做到这一点,必须使用递归,因为必须修改每行的位向量,而XSLT作为函数式编程语言无法修改“变量”
下面的样式表显示了具有3列的表的想法
<xsl:template match="TABLE2">
<tbody>
<xsl:call-template name="processRows">
<xsl:with-param name="rows" select="ROW"/>
</xsl:call-template>
</tbody>
</xsl:template>
<xsl:template name="processRows">
<xsl:param name="rows"/>
<xsl:param name="index" select="1"/>
<!-- Bit vector for the columns -->
<xsl:param name="col1" select="0"/>
<xsl:param name="col2" select="0"/>
<xsl:param name="col3" select="0"/>
<xsl:variable name="cellsBefore2">
<xsl:choose>
<xsl:when test="$col1 > 0">0</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="cellsBefore3">
<xsl:choose>
<xsl:when test="$col2 > 0">
<xsl:value-of select="$cellsBefore2"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$cellsBefore2 + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<row>
<xsl:if test="$col1 = 0">
<entry colname="1">
<xsl:value-of select="$rows[$index]/CELL[1]/text()"/>
</entry>
</xsl:if>
<xsl:if test="$col2 = 0">
<entry colname="2">
<xsl:value-of select="$rows[$index]/CELL[$cellsBefore2 + 1]/text()"/>
</entry>
</xsl:if>
<xsl:if test="$col3 = 0">
<entry colname="3">
<xsl:value-of select="$rows[$index]/CELL[$cellsBefore3 + 1]/text()"/>
</entry>
</xsl:if>
</row>
<xsl:if test="$index < count($rows)">
<xsl:call-template name="processRows">
<xsl:with-param name="rows" select="$rows"/>
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="col1">
<xsl:choose>
<xsl:when test="$col1 > 0">
<xsl:value-of select="$col1 - 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number($rows[$index]/CELL[1]/@ROWSPAN) - 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="col2">
<xsl:choose>
<xsl:when test="$col2 > 0">
<xsl:value-of select="$col2 - 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number($rows[$index]/CELL[$cellsBefore2 + 1]/@ROWSPAN) - 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="col3">
<xsl:choose>
<xsl:when test="$col3 > 0">
<xsl:value-of select="$col3 - 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number($rows[$index]/CELL[$cellsBefore3 + 1]/@ROWSPAN) - 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
0
1.
如您所见,问题在于列数是硬编码的。如果希望有任意数量的列,则需要能够生成并传递任意长度的列表作为参数。这只有在XSLT2.0中才可能实现。但是,如果您使用的是XSLT 1.1,则可以改用EXSLT(如果您的环境中有),将列表作为结果树写入变量中,并在此变量上使用EXSLT:node-set()来获得可用的列表。我从未使用过
rowspan
,也不知道它的含义。XSLT和XPath专家不应该知道任何特定的XML词汇表,即使它恰好是XHTML。请用行span
含义的定义编辑问题。考虑到输出可能取决于任何数量的先前行
元素,这看起来非常棘手。我脑海中浮现出一个想法:使用递归模板依次处理每一行,并从前几行传入一个“已占用”行列表,但这需要一些时间来充实。快速提问:您是否也要使用COLSPAN
标记?如果是这样的话,事情就复杂多了。@Flynn1179没有使用COLSPAN,我使用了namest和namend。谢谢!这是非常有帮助的。你能告诉我如何将任意长度的列表作为参数传递吗?我想使用另一种解决方案更容易,它通过将向量连接成字符串来解决问题。