我正在使用表条目元素,并希望在通过以下测试的同一个表中获取所有前面的条目元素:
parent::row/preceding-sibling::row/entry
[@morerows >= count(parent::row/following-sibling::row
[not(preceding-sibling::row/entry
[generate-id() = $id])])]
[count(preceding-sibling::entry
[not(@morerows)]) + 1 = count(current()/preceding-sibling::entry) + 1]
XPath给了我想要的结果,但它很慢......我想使用密钥但是遇到了问题。
我按如下方式定义了一个键:
<xsl:key name="moreRowsEntry" match="entry[@morerows]" use="."/>
虽然键确实检索了具有morerows属性的所有条目元素,但实际上我只需要它来检索同一祖先表中的那些元素。同样,我对如何测试更多的价值感到不知所措。我尝试过这样的事情:
<xsl:for-each select="key('moreRowsEntry', (@morerows >= count(parent::row/following-sibling::row[not(preceding-sibling::row/entry[generate-id() = $id])])) and (count(preceding-sibling::entry[not(@morerows)]) + 1 = count(current()/preceding-sibling::entry) + 1))">
我必须使用XSL 1.0来做到这一点。任何和所有的帮助表示赞赏。
进一步的信息:
我正在将CALS表转换为OOXML。对于CALS表中我知道缺少单元格的行中的每个条目,我需要添加这些附加单元格。对于这些条目元素,我有一个$id
,它是元素的generate-id()值。
然后我从上面得到了XPath,它测试表格前面几行中的任何入口元素(parent::row/preceding-sibling::row/entry
),它们的morerows属性大于或等于它自身之间的行数。 id为$id
的条目,位于应插入空单元格的正确位置(count(preceding-sibling::entry[not(@morerows)]) + 1 = count(current()/preceding-sibling::entry) + 1
)
简化的样本输入:
<table>
<tbody>
<row>
<entry morerows="2">A</entry>
<entry morerows="1">B</entry>
<entry>C</entry>
<entry>D</entry>
</row>
<row>
<entry>E</entry>
<entry>F</entry>
</row>
<row>
<entry>G</entry>
<entry>H</entry>
<entry>I</entry>
</row>
<row>
<entry>J</entry>
<entry>K</entry>
<entry>L</entry>
<entry>M</entry>
</row>
</tbody>
</table>
简化样品输出:
<w:tbl>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>A</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>B</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>C</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>D</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>E</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>F</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>G</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>H</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>I</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>J</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>K</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>L</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>M</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>
输入使用morerows属性指定跨区行。输出要求为每个生成的单元格插入实际单元格<w:vMerge/>
元素。
另一个样本:
在此示例中,还有合并列(由namest和nameend属性指定),这些列也必须考虑到帐户:
<table>
<tgroup cols="7">
<colspec colname="col1"/>
<colspec colname="col2"/>
<colspec colname="col3"/>
<colspec colname="col4"/>
<colspec colname="col5"/>
<colspec colname="col6"/>
<colspec colname="col7"/>
<tbody>
<row>
<entry morerows="5">A</entry>
<entry morerows="1">B</entry>
<entry morerows="1">C</entry>
<entry>D</entry>
<entry>E</entry>
<entry>F</entry>
<entry>G</entry>
</row>
<row>
<entry>2D</entry>
<entry>2E</entry>
<entry>2F</entry>
<entry>2G</entry>
</row>
<row>
<entry morerows="1">3B</entry>
<entry morerows="1">3C</entry>
<entry>3D</entry>
<entry>3E</entry>
<entry>3F</entry>
<entry>3G</entry>
</row>
<row>
<entry>4D</entry>
<entry>4E</entry>
<entry>4F</entry>
<entry>4G</entry>
</row>
<row>
<entry morerows="1" nameend="col6" namest="col2">3G - 4G</entry>
<entry morerows="1">5G</entry>
</row>
</tbody>
</tgroup>
</table>
输出:
<w:tbl>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge w:val="restart"/>
</w:tcPr>
<w:p>
<w:r>
<w:t>A</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge w:val="restart"/>
</w:tcPr>
<w:p>
<w:r>
<w:t>B</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge w:val="restart"/>
</w:tcPr>
<w:p>
<w:r>
<w:t>C</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>D</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>E</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>F</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>G</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>2D</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>2E</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>2F</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>2G</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge w:val="restart"/>
</w:tcPr>
<w:p>
<w:r>
<w:t>3B</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge w:val="restart"/>
</w:tcPr>
<w:p>
<w:r>
<w:t>3C</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>3D</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>3E</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>3F</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>3G</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>4D</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>4E</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>4F</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>4G</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</w:tc>
<w:tc>
<w:tcPr>
<w:gridSpan w:val="5"/>
</w:tcPr>
<w:p>
<w:r>
<w:t>3G - 4G</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>5G</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>
答案 0 :(得分:2)
如果您的处理器可以执行the exslt:node-set
function或等同于that provided by msxml,那么您可以使用尾递归模板在程序上对此进行攻击。我知道,丑陋,完全反对我们通常建议的XSLT,但在这种情况下,我认为有效地做到这一点的唯一方法是将信息从一行传递到下一行。假设表的第一个行对于每一列总是有一个entry
,那么这样的事情如何:
<xsl:template match="tbody">
<w:tbl>
<xsl:apply-templates select="row[1]">
<xsl:with-param name="rowSpec">
<!-- build the row info structure assuming that the first row has
the right number of entry elements. Nothing spans into the
first row, so they all get @span=0 -->
<xsl:for-each select="row[1]/entry">
<r span="0" />
</xsl:for-each>
</xsl:with-param>
</xsl:apply-templates>
</w:tbl>
</xsl:template>
<xsl:template match="row">
<xsl:param name="rowSpec" />
<xsl:variable name="theRow" select="." />
<w:tr>
<!-- build up the output for this row -->
<xsl:for-each select="exsl:node-set($rowSpec)/r">
<w:tc>
<xsl:choose>
<xsl:when test="@span = 0">
<!-- this row has an entry for the column -->
<xsl:apply-templates select="$theRow/entry[
count(current()/preceding-sibling::r[@span = 0]) + 1]" />
</xsl:when>
<xsl:otherwise>
<!-- this column spans from the previous row -->
<w:tcPr>
<w:vMerge/>
</w:tcPr>
<w:p/>
</xsl:otherwise>
</xsl:choose>
</w:tc>
</xsl:for-each>
</w:tr>
<!-- process the next row with recalculated spans -->
<xsl:apply-templates select="following-sibling::row[1]">
<xsl:with-param name="rowSpec">
<xsl:for-each select="exsl:node-set($rowSpec)/r">
<xsl:choose>
<xsl:when test="@span = 0">
<!-- we had an entry element for this column, use its @morerows
as the next span, or 0 if it doesn't have one -->
<xsl:choose>
<xsl:when test="$theRow/entry[
count(current()/preceding-sibling::r[@span = 0]) + 1]/@morerows">
<r span="{$theRow/entry[
count(current()/preceding-sibling::r[@span = 0]) + 1]/@morerows}" />
</xsl:when>
<xsl:otherwise>
<r span="0" />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- we didn't have an entry for this column, it was a span from the
previous row - subtract 1 from the span when we pass on to
the next row -->
<r span="{@span - 1}" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="entry">
<w:p>
<w:r>
<w:t><xsl:value-of select="." /></w:t>
</w:r>
</w:p>
</xsl:template>
(哇,当我开始它时,结果比我预期的要复杂得多,但我已经测试了它,它似乎正常工作)。这里的想法是我们构建一个结构,该结构编码每个列在处理该行时仍需要跨越的行数。所以对于第一行我们将
<r span="0"/>
<r span="0"/>
<r span="0"/>
<r span="0"/>
然后第二次它将是
<r span="2"/>
<r span="1"/>
<r span="0"/>
<r span="0"/>
第三个
<r span="1"/>
<r span="0"/>
<r span="0"/>
<r span="0"/>
等。对于每一行,我们将遍历此结构,而不是遍历entry
元素本身。
编辑:现在你已经改变了问题,所以你需要考虑列跨度和行跨度的可能性,它会变得更加混乱。由于我们致力于使用节点集函数,我会考虑LarsH暗示的两步方法,您首先将列跨越条目扩展为实际entry
元素(具有某些属性)将它们标识为这样)然后处理扩展版本的XML代替原始版本。
答案 1 :(得分:1)
在我的脑海中,我首先会使用一个与其祖先表相匹配的键:
<xsl:key name="moreRowsEntriesByTable" match="entry[@morerows]"
use="generate-id(ancestor::table[1])" />
希望您可以使用它来缩小必须在XPath中处理的节点集。但我承认我并没有真正理解你试图过滤的条件,所以我并不认为以上是适用的。
Re:“我无法弄清楚如何包含我的XPath条件并获得结果。”我经常对XSLT 1.0感到沮丧,因为它试图将一个键与前面的::或后面的::轴组合起来。它不能简洁地完成。
您可以通过在需要的位置插入以下谓词来实现它:
[count(key('moreRowsEntriesByTable', $table-id) | .) =
count(key('moreRowsEntriesByTable', $table-id)]
其中$ table-id是current()节点的祖先表的id:
<xsl:variable name="table-id" select="generate-id(ancestor::table[1])" />
但这可能比根本不使用密钥慢:
[generate-id(ancestor::table[1]) = $table-id]
您可以将此谓词附加到XPath表达式中的每个entry
节点测试中。但同样,我不确定它会对性能有所帮助。
node-set()
扩展函数将一个模板的输出转换为可作为另一个模板输入的节点集。
在第一个转换中,您可以向每个表格单元格添加属性以帮助进行计算,例如......
@row -- row number of this entry in current table (position() of parent::row)
@index -- position of this entry among siblings in the parent::row, taking into
account their colspans if necessary.
@table-id -- generate-id(ancestor::table[1])
这个想法将是摊销(如果我正确应用该术语)...... 如果这些值是每个条目计算一次,在第一阶段,而不是很多次,在复杂的XPath表达式中,这可能会使事情变得更快。也许还有其他属性会更有帮助。
您可以在第二次转换中删除(不复制)这些辅助属性。
这是一个粗略的建议,但也许它有助于指出一个好的解决方案。