递归计算嵌套标签

时间:2017-01-10 21:09:02

标签: xml xslt

我有一个带有<section>标签的文档格式,可以无限嵌套。每个部分都包含段落和表格等内容,但新部分的第一个元素可能是任何部分,包括另一部分。

<doc>
  <section name="one">
    <p> Some text about <i>this</i> section.</p>
    <section name="one.one">
      <section name="one.one.one">
        <p>Other text</p>
      </section>
    </section>
  </section>
</doc>

我将文档格式转换为markdown。似乎应用大多数标签的自然方式是使用如下结构:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="doc|p">
    <xsl:apply-templates select="* | text()"/>
  </xsl:template>
  <xsl:template match="i">
    <xsl:text>_</xsl:text>
    <xsl:apply-templates select="* | text()"/>
    <xsl:text>_</xsl:text>
  </xsl:template>
</xsl:stylesheet>

但是,对于<section>标记,我需要跟踪嵌套深度以构建节名称。似乎这样做的自然方式是使用<xsl:call-template><xsl:param>

<xsl:template match="section">
  <xsl:param name="depth" select="1"/>
  <!-- do stuff to write the section name -->
  <xsl:apply-templates select="* | text()"/>
</xsl>

我遇到的问题是需要使用<xsl:call-template>而不是<xsl:apply-templates>来触发,以便增加参数。

<xsl:call-template name="section">
  <xsl:with-param name="depth" select="$depth+1"/>
</xsl:call-template>

我无法为apply-templates提供参数,但我也无法使用递归section启动doc(或call-template)模板,因为新的部分可能不是第一个标签。

有没有办法解决这个问题,让我使用call-template,但只保留xslt通常只使用简单apply-templates标记的标记顺序?

2 个答案:

答案 0 :(得分:2)

以下是使用xsl:numberlevel="multiple")确定深度的示例。这样会为您提供类似1.1.1.(第三级)的内容,但您可以将.翻译为#(并将数字翻译为空)。

XML输入

<doc>
    <section name="one">
        <p> Some text about <i>this</i> section.</p>
        <section name="one.one">
            <section name="one.one.one">
                <p>Other text</p>
            </section>
        </section>
    </section>
</doc>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="chars" select="'.0123456789'"/>

  <xsl:template match="i">
    <xsl:value-of select="concat('_',.,'_')"/>
  </xsl:template>

  <xsl:template match="p">
    <xsl:apply-templates/>
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>

  <xsl:template match="section">
    <xsl:variable name="depth">
      <xsl:number level="multiple" format="1."/>
    </xsl:variable>
    <xsl:value-of select="translate($depth,$chars,'#')"/>
    <xsl:value-of select="concat(@name,'&#xA;')"/>
    <xsl:apply-templates/>
  </xsl:template>

</xsl:stylesheet>

<强>输出

#one
 Some text about _this_ section.
##one.one
###one.one.one
Other text

修改

这是另一种计算像迈克尔凯建议的祖先或自我的方式(呃!)......

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="i">
    <xsl:value-of select="concat('_',.,'_')"/>
  </xsl:template>

  <xsl:template match="p">
    <xsl:apply-templates/>
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>

  <xsl:template match="section">
    <xsl:for-each select="ancestor-or-self::section">#</xsl:for-each>
    <xsl:value-of select="concat(@name,'&#xA;')"/>
    <xsl:apply-templates/>
  </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

首先,您可以使用apply-templates传递params,就像使用call-template一样简单:apply-templates允许使用xsl:with-param子元素。

其次,您不需要参数来确定嵌套的深度,因为您可以使用向上导航的XPath表达式,例如count(ancestor::section)