XSLT - 使用数组等数据结构

XSLT - use data structures like arrays

我正在使用 XSLT 进行 html 到 xml 的转换。我发现 html table(合并单元格)转换为 xml.

是一项相当困难的任务

这是场景,

我的输入htmltable,

<table>
    <thead>
        <tr>
            <td rowspan="3">Date</td>
            <td colspan="5">Customer Price Index</td>
            <td rowspan="3"> private consumption chain price </td>
            <td colspan="2"> Other consumer price mesure </td>
        </tr>
        <tr>
            <td rowspan="2"> All groups </td>
            <td rowspan="2"> Excluding volatile items </td>
            <td colspan="3">Market prices excluding volatile items</td>
            <td colspan="2"> Based on seasonally adjusted quntity price changers </td>
        </tr>
        <tr>
            <td>Goods</td>
            <td>Services</td>
            <td>Total</td>
            <td> weihgted median </td>
            <td>Trimmed mean</td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>2003/04</td>
            <td colspan="8">content</td>
        </tr>
        <tr>
            <td>Dec</td>
            <td>2.4</td>
            <td>2.4</td>
            <td>1.6</td>
            <td>2.2</td>
            <td>1.8</td>
            <td>1.0</td>
            <td>2.0</td>
            <td>2.5</td>
        </tr>
    </tbody>
</table>

需要xml输出,

  <table>
        <thead>
            <row>
                <data namest="1" morerows="2">
                    <p>Date</p>
                </data>
                <data namest="2" nameend="6">
                    <p>Consumer price index</p>
                </data>
                <data namest="7" morerows="2">
                    <p>Private consumption chain price index</p>
                </data>
                <data namest="8" nameend="9">
                    <p>Other consumer price mesure</p>
                </data>
            </row>
            <row>
                <data namest="2" morerows="1">
                    <p>All groups</p>
                </data>
                <data namest="3" morerows="1">
                    <p>Excluding volatile items</p>
                </data>
                <data namest="4" nameend="6">
                    <p>Market prices excluding volatile items</p>
                </data>
                <data namest="8" nameend="9">
                    <p>Based on seasonally adjusted quntity price changers</p>
                </data>
            </row>
            <row>
                <data namest="4">
                    <p>Goods</p>
                </data>
                <data namest="5">
                    <p>Services</p>
                </data>
                <data namest="6">
                    <p>Total</p>
                </data>
                <data namest="8">
                    <p>Weighted median</p>
                </data>
                <data namest="9">
                    <p>Trimmed mean</p>
                </data>
            </row>
        </thead>
        <tbody>
            <row>
                <data namest="1">
                    <p>2003/04</p>
                </data>
                <data namest="2" nameend="9">
                    <p>content</p>
                </data>
            </row>
            <row>
                <data namest="1">
                    <p>Dec</p>
                </data>
                <data namest="2">
                    <p>2.4</p>
                </data>
                <data namest="3">
                    <p>2.4</p>
                </data>
                <data namest="4">
                    <p>1.6</p>
                </data>
                <data namest="5">
                    <p>2.2</p>
                </data>
                <data namest="6">
                    <p>1.8</p>
                </data>
                <data namest="7">
                    <p>1.0</p>
                </data>
                <data namest="8">
                    <p>2.8</p>
                </data>
                <data namest="9">
                    <p>2.5</p>
                </data>
            </row>
        </tbody>
</table>

如您所见,在输入 html.

中,垂直单元格合并表示为 rowspan attr,水平合并表示为 colspan attr

并且在预期输出中 namest attr 表示单元格起始列号,morerows attr 表示向下(垂直)单元格合并的数量,nameend attr 表示最后一个单元格列number(水平合并)。

这种情况可以通过使用数据结构(二维数组)的其他语言来解决,但我正在努力寻找使用 XSLT 完成此任务的有效方法。

我写了下面的 xsl 来完成这个任务,它适用于第一行,但对于其他行,这种方法太复杂了。

 <xsl:template match="td[parent::tr[not(preceding::tr)]]">

        <xsl:variable name="pre_rowspan" select="number(format-number(count(preceding-sibling::td[@rowspan])+1, '#0', 'myformat'))"/>
        <xsl:variable name="pre_colspan" select="number(format-number(preceding-sibling::td[@colspan]/@colspan, '#0', 'myformat'))"/>
        <xsl:variable name="numberof_pre_rowspan" select="number(format-number(count(preceding-sibling::td[@rowspan])+1, '#0', 'myformat'))"/>

        <data>
            <xsl:attribute name="namest" select="number($pre_rowspan + $pre_colspan)"/>
            <xsl:if test="@rowspan">
                <xsl:attribute name="morerows" select="number(@rowspan)-1"/>
            </xsl:if>
            <xsl:if test="@colspan">
                <xsl:attribute name="nameend" select="number(@colspan)+number(format-number(count(preceding-sibling::td[@rowspan]), '#0', 'myformat'))+number(format-number(number(preceding-sibling::td[@colspan]/@colspan), '#0', 'myformat'))"/>
            </xsl:if>
            <xsl:if test="@rowspan and @colspan">
                <xsl:attribute name="nameend" select="$pre_rowspan"/>
            </xsl:if>

            <p>
                <xsl:apply-templates/>
            </p>

        </data>
    </xsl:template>

所以,谁能建议我如何使用 xslt 完成此任务的方法。 (使用数据结构或任何其他方法)

是的,这是一个相当困难的问题,我只是想勾勒出一个方法。

这将涉及兄弟递归,首先通过tr中的兄弟td,然后通过兄弟tr。当你通过递归移动时,我认为你需要传递一个表示哪些单元格被占用的数据结构,我建议将其作为字符串序列来执行,例如("XXX", "X-X", "--X")表示第1行的前三个单元格被占用,第2行的第1、3个单元格被占用,以此类推。

如果我正确理解 rowspan 和 colspan,规则是对于第 N 个 tr 中的 td,它的起始行始终为 N,并且它将占据第一个可用列,以便所有必需的单元格是免费的,前提是它位于同一行开始的所有先前单元格的右侧。

所以我建议当你的递归到达一个特定的 td 时,你传递三个参数:行号 $row,该行中的第一个空闲列 $firstFreeCol,以及占用率 table,如上所述的字符串序列。给定这三个值,加上 rowspan 和 colspan 的值,然后测试(在占用 table 中)$row 和($row+ @rowspan - 1)之间的每一行是否具有 $firstFreeCol 和($)之间的每一列firstFreeCol + @colspan - 1) 免费。如果没有,重复 $firstFreeCol $firstFreeCol + 1。如果它是空闲的,输出这个单元格,及其分配的坐标,并继续下一个 $firstFreeCol 设置为 $firstFreeCol + $colSpan 和占用 table 已更新以将占用的单元格设置为 "X"s。

不知道你对使用递归实现这种效果有多熟悉。在我的书中,我做了 "Knight's Tour" 的示例,其明确目的是说明像这样的复杂算法在 XSLT 中是完全可行的,但如果您是函数式编程的新手,那么这需要一段时间自然。 Knight's Tour 也有类似的需要,即在有限的可用工具集的情况下对数据结构进行创新(使用 XSLT 3.0 中的映射和数组,这一切都变得更容易......)。您将需要一系列实用功能,例如,这是一个(未经测试的)功能,可将占用 table 中的特定单元格标记为已占用,以及 returns 新占用 table:

<xsl:function name="f:set-occupied-cell" as="xs:string*">
  <xsl:param name="occupancy" as ="xs:string*"/>
  <xsl:param name="row" as="xs:integer"/>
  <xsl:param name="col" as="xs:integer"/>
  <xsl:sequence select="
     for $i in 1 to $row - 1
     return if ($i gt count($occupancy)) then "" else $occupancy[$i]"/>
  <xsl:variable name="target-row" select="$occupancy[$row]"/>
  <xsl:sequence select="concat(
    string-join(
     for $i in 1 to $col - 1
     return if ($i gt string-length($target-row) 
            then "-" 
            else substring($target-row, $i, 1), ''),
    "X",
    substring($target-row, $col+1)"/>
  <xsl:sequence select="subsequence($occupancy, $row+1)"/>
</xsl:function>