如何在for-each中将模板应用于每个选定的节点?

7

我知道我在这里漏掉了一些东西。 在下面的XSLT转换中,实际结果与期望结果不匹配。

for-each 中,我想将 match="track" 模板应用于每个选定的 track 元素。 如果我正确理解了XSLT,则当前设置仅针对每个选定的 track 元素的子节点与模板匹配,而不是 track 元素本身。

如何使 track 元素按预期通过模板? 我需要重新考虑整个方法吗?

注意:使用PHP执行转换。 为简洁起见,已省略XML声明。

XML文档:

<album>
    <title>Grave Dancers Union</title>
    <track id="shove">Somebody To Shove</track>
    <track id="gold">Black Gold</track>
    <track id="train">Runaway Train</track>
    <producer>Michael Beinhorn</producer>
</album>

XSL 样式表:

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/album">
        <ol>
            <xsl:for-each select="track">
                <li><xsl:apply-templates/></li>
            </xsl:for-each>
        </ol>
    </xsl:template>

    <xsl:template match="track">
        <a href="{@id}"><xsl:apply-templates/></a>
    </xsl:template>
</xsl:stylesheet>

结果:

<ol>
    <li>Somebody To Shove</li>
    <li>Black Gold</li>
    <li>Runaway Train</li>
</ol>

期望结果:

<ol>
    <li><a href="shove">Somebody To Shove</a></li>
    <li><a href="gold">Black Gold</a></li>
    <li><a href="train">Runaway Train</a></li>
</ol>
4个回答

15

我同意'ndim'的观点,你应该重新构建你的XSLT,摒弃xsl:for-each循环。

或者,你可以修改xsl:apply-templates以在xsl:for-each中选择当前track节点。

<xsl:for-each select="track">
   <li>
      <xsl:apply-templates select="." />
   </li>
</xsl:for-each>

如果需要的话,保留xsl:for-each至少可以让您按照不同的顺序对曲目进行排序。


排序做得很好。我想由于排序,这应该是最佳解决方案。 - ndim
我本来就以为我已经尝试过这个了,但我猜没有。不过这正是我所需要的! - Jakob

4

如果您不需要for-each方法提供的排序功能,我会稍微重构一下:

<xsl:template match="/album">
  <ol>
    <xsl:apply-templates select="track"/>
  </ol>
</xsl:template>

<xsl:template match="track">
  <li><a href="{@id}"><xsl:apply-templates/></a></li>
<xsl:template>

在我看来,这个更简短明了。

我想你的

    <xsl:for-each select="track">
       <li><xsl:apply-templates/></li>
    </xsl:for-each>

使用for-each遍历所有的轨道元素,然后将默认规则应用于其后代。因此,for-each的内容与match="track"模板具有相同的上下文节点,因此match="track"模板永远不会匹配。
如果确实想以这种方式使用for-each,则需要更改以下两个方面中的任意一个:
1. 向match="track"模板添加name="track"属性,并从for-each内部使用<xsl:call-template/>(我的想法,比Tim C的还糟糕)。 2. 使用Tim C的解决方案,即使用<xsl:apply-templates select="."/>。这样做的好处是避免命名并保留对轨道进行排序的可能性。

重构解决方案似乎不够模块化(它不允许我在页面/站点的其他位置使用相同的样式表来包含<dl><table>中的轨迹),但你可能是对的,这是这个例子最顺畅的解决方案。 - Jakob
@Jakob 我知道这已经是古老的方法,但我想指出你可以使用 mode 属性来选择相同节点的另一个模板。例如 <xsl:apply-templates mode="table"><xsl:template match="track" mode="table">...</xsl:template> - janek37

0

我认为使用apply-templates和模板模式是更清晰的解决方案:

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>

  <xsl:template match="/">
    <body>
      <xsl:apply-templates select="album" mode="ol" />
    </body>
  </xsl:template>

  <xsl:template match="album" mode="ol">
    <ol>
      <xsl:apply-templates select="track" mode="li" />
    </ol>
  </xsl:template>

  <xsl:template match="track" mode="li">
    <li>
      <xsl:apply-templates select="." />
    </li>
  </xsl:template>

  <xsl:template match="track">
    <a href="{@id}">
      <xsl:value-of select="." />
    </a>
  </xsl:template>


</xsl:stylesheet>

结果为:

<body>
  <ol>
    <li>
      <a href="shove">Somebody To Shove</a>
    </li>
    <li>
      <a href="gold">Black Gold</a>
    </li>
    <li>
      <a href="train">Runaway Train</a>
    </li>
  </ol>
</body>

0

for-each 语句会将上下文节点从 album 更改为 track。apply-templates 默认情况下将模板应用于上下文节点的子节点,在您的情况下,这些子节点是 track 元素的子节点。因此,您匹配 'track' 的模板永远不会被命中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接