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>
我正在使用 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>