1

我正在使用表条目元素,并希望在同一个表中获取通过以下测试的所有前面的条目元素:

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 属性的所有条目元素,但实际上我只需要它来检索同一个祖先表中的那些元素。同样,我不知道如何测试 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>
4

2 回答 2

2

如果您的处理器可以执行exslt:node-set功能或类似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 的扩展版本来代替原始版本。

于 2013-07-24T15:40:13.780 回答
1

在我的脑海中,我首先会使用一个匹配其祖先表条目的键:

<xsl:key name="moreRowsEntriesByTable" match="entry[@morerows]"
   use="generate-id(ancestor::table[1])" />

希望您可以使用它来缩小必须在 XPath 中处理的节点集。但我承认我还没有真正理解你试图过滤的条件,所以我不肯定上述内容是否适用。

更新

回复:“我不知道如何包含我的 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]

您可以将此谓词附加到entryXPath 表达式中的每个节点测试。但同样,我不确定它是否有助于提高性能。

选择

相反,我建议进行两阶段转型。您的处理上下文是否允许这样做?为此,您可以运行两个样式表,将第一个样式表的输出通过管道传输到第二个样式表的输入;或者使用通用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 表达式中,这可能会使事情变得相当多快点。也许还有其他属性会更有帮助。

您可以在第二次转换中删除(而不是复制)这些辅助属性。

这是一个粗略的建议,但也许它有助于指出一个好的解决方案。

于 2013-07-23T21:12:51.900 回答