如何使用XSLT在嵌套元素中维护计数?

时间:2009-11-16 11:06:00

标签: xslt

与问题here相关。

我有以下格式的XML输入:

<book>
    <chapter>
        ...
    </chapter>
</book>  

我目前正在输出类似的内容:

<div class="book" id="book-3">
    <div class="chapter>
        ...
    </div>
</div>

使用规则<div class="book" id="book-{position()}">创建图书ID。这样id的值就会在每个<book>节点上递增。

我想输出表格:

<div class="book" id="book-3">
    <div class="chapter" id="book-3-chapter-7">
        ...
    </div>
</div>

我一直在尝试使用从父级检索id属性并将其输出到子级中的规则。只是当我问这个问题时,我才意识到,"../@id"永远不会匹配,因为源XML中的属性不存在,只有输出。

尝试将parent属性用作id的一部分可能不是“XSLT方式”。因此,即使有办法访问输出,这可能是错误的方法。

一旦用他们的id创建了章节标签,我想要遵循相同约定的章节元素的子节点(因此它们将具有<div id="book-2-chapter12-verse212">)。 id将由单独的Java应用程序用于链接到文档,因此遵循约定很重要(即生成的id不起作用)。

使用XSLT创建这个嵌套递增计数器的最佳方法是什么?

P.S。这个问题的标题可能与我真正想要提出的问题相差甚远,但我不知道XSLT是否足以提出正确的问题。请随时编辑或建议更好的标题。


编辑:虽然对于原始问题有很好的答案,正如我所说的,我不确定如何正确地提出这个问题。在得到我应该问的问题的答案之后,我已经修改了这个问题。

这意味着有一些答案可以解决我的(糟糕的)原始问题以及一个理解我发现难以表达的意图的答案。我接受了后者。

我希望得到答复的其他人认为这是可以接受的,如果有任何建议如何处理首先提出错误的问题,我会很高兴听到他们的意见。

3 个答案:

答案 0 :(得分:3)

你应该看一下xsl:number element。它有一堆格式化和分组结构来完成你试图识别的计数和标签的种类。

This XML.com article解释了如何使用某些属性为书籍,章节,部分等生成多级数字(计数)。

类似的东西:

<xsl:number format="1. " level="multiple" count="book|chapter|verse"/>

在该节目的模板中应用会产生: 2.12.212

答案 1 :(得分:2)

我可能会在模板中添加一个参数,并在apply-templates中使用with-param将其传递下去。避免所有歧义。

<xsl:template match="..."> <!-- the child match -->
    <xsl:parameter name="parentId"/>
    <child id="{$parentId}-@id">
      <!-- code involving $parentId -->
    </child>
</xsl:template>

等等:

<xsl:apply-templates select="child">
    <xsl:with-param name="parentId" select="@id"/>
</xsl:apply-templates>

另一种选择是在孩子身上使用..等 - 也许在@id(来自两个地方)使用模板匹配,以避免重复。

答案 2 :(得分:0)

我会为此目的使用通用函数:

<xsl:template match="*">
  <xsl:copy>
    <xsl:attribute name="id">
      <xsl:call-template name="compute-id"/>
    </xsl:attribute>
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>

<xsl:template name="compute-id">
  <xsl:param name="curr-elem" select="."/>
  <xsl:param name="id" select="''"/>
  <xsl:choose>
    <xsl:when test="$curr-elem/..">
    <xsl:call-template name="compute-id">
      <xsl:with-param name="curr-elem" select="$curr-elem/.."/>
      <xsl:with-param name="id">
        <xsl:value-of select="concat(name($curr-elem), count($curr-elem/preceding-sibling::*[name()=name($curr-elem)])+1)"/>
        <xsl:if test="$id != ''">
          <xsl:value-of select="concat('-', $id)"/>
        </xsl:if>
      </xsl:with-param>
    </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$id"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>