在嵌套模板中添加标点符号

Adding punctuation in nested templates

我正在尝试在 <album> 中添加带有逗号和 space 的元素,如果它不是父元素 <recording> 中的最后一个 <album>

我有一个用于 <recording> 的类似模板,可以按预期工作。但是,我无法使 <album> 标点符号的第二个模板正常工作。

我认为这与现有的第一个模板有关...

输入XML

<sample>
<collection>
    <recording>
        <artist>Radiohead</artist>
        <album>OK Computer</album>
    </recording>
    <recording>
        <artist>Tori Amos</artist>
        <album>Boys For Pele</album>
        <album>To Venus And Back</album>
    </recording>
</collection>

示例 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">

<xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="collection">
    <xsl:copy>
        <xsl:for-each select="recording">
            <xsl:copy>
                <xsl:apply-templates select="node() | @*"/>
            </xsl:copy>
            <xsl:if test="position()!=last()">
                <xsl:element name="x">, </xsl:element>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

<xsl:template match="recording">
    <xsl:copy>
        <xsl:for-each select="album">
            <xsl:copy>
                <xsl:apply-templates select="node() | @*"/>
            </xsl:copy>
            <xsl:if test="position()!=last()">
                <comma>, </comma>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

但是我的输出是

<sample>
<collection>
    <recording>
        <artist>Radiohead</artist>
        <album>OK Computer</album>
    </recording><x>, </x>
    <recording>
        <artist>Tori Amos</artist>
        <album>Boys For Pele</album>
        <album>To Venus And Back</album>
    </recording>
</collection>

而不是我想要的(注意<x>,在第一个<album>之后在第二个<recording>

<sample>
    <collection>
        <recording>
            <artist>Radiohead</artist>
            <album>OK Computer</album>
        </recording><x>, </x>
        <recording>
            <artist>Tori Amos</artist>
            <album>Boys For Pele</album><x>, </x>
            <album>To Venus And Back</album>
        </recording>
    </collection>
</sample>

将你的第二个(匹配集合)模板更改为

<xsl:template match="collection">
    <xsl:copy>
        <xsl:for-each select="recording">
            <xsl:apply-templates select="."/>
            <xsl:if test="position()!=last()">
                <xsl:element name="x">, </xsl:element>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

并将您的录音模板修改为

<xsl:template match="recording">
    <xsl:copy>
        <xsl:apply-templates select="(node() | @*) except album"/>
        <xsl:for-each select="album">
            <xsl:copy>
                <xsl:apply-templates select="node() | @*"/>
            </xsl:copy>
            <xsl:if test="position()!=last()">
                <x>, </x>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

删除 xsl:copy 元素并将其替换为 xsl:apply-templates 元素并将对其他录音子项的调用移动到 recording 模板中。

就目前而言,您正在遍历记录元素并将模板应用于内容节点。因此,您永远不允许 recording 模板匹配任何内容。通过将模板应用于当前上下文节点,我们正在使用 recording 节点本身而不是其内容,从而允许 recording 模板匹配。

此外,在您的 recording 模板中,您要添加 comma 元素而不是 x 元素。我已将这些更改为上面的 x 元素,因为这似乎是您想要的。


此外,您的第一个 xsl:for-each 甚至都不需要。如果我们将位置测试从 collection 模板移动到 recording 模板,我们可以简化 collection 模板避免 xsl:for-each:

<xsl:template match="collection">
    <xsl:copy>
        <xsl:apply-templates select="recording"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="recording">
    <xsl:copy>
        <xsl:apply-templates select="(node() | @*) except album"/>
        <xsl:for-each select="album">
            <xsl:copy>
                <xsl:apply-templates select="node() | @*"/>
            </xsl:copy>
            <xsl:if test="position()!=last()">
                <x>, </x>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
    <xsl:if test="position()!=last()">
        <xsl:element name="x">, </xsl:element>
    </xsl:if>       
</xsl:template>

我们甚至可以对专辑做同样的事情,消除两个 xsl:for-each 元素

<xsl:template match="collection">
    <xsl:copy>
        <xsl:apply-templates select="recording"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="recording">
    <xsl:copy>
        <xsl:apply-templates select="(node() | @*) except album"/>
        <xsl:apply-templates select="album"/>
    </xsl:copy>
    <xsl:if test="position()!=last()">
        <xsl:element name="x">, </xsl:element>
    </xsl:if>       
</xsl:template>

<xsl:template match="album">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
    <xsl:if test="position()!=last()">
        <x>, </x>
    </xsl:if>       
</xsl:template>

以上所有内容都会复制完全为空白的元素之间的文本节点,这可能是不可取的。将以下内容添加到样式表的顶部将防止出现这种情况:

<xsl:strip-space elements="*"/>

此 post 中的所有示例均使用带有附加 xsl:strip-space 元素的 OP 原始示例进行测试,<xsl:output method="xml" indent="yes"/> 使用 Saxon-HE 9.7.0.2J 并产生相当于 OP 所需输出的结果,直至缩进(x 元素出现在它们自己的行上)。

我要做的是创建一个匹配 recordingalbum 的模板,如果它不是同类中的第一个,则输出 <x>, </x>.

XML 输入

<sample>
    <collection>
        <recording>
            <artist>Radiohead</artist>
            <album>OK Computer</album>
        </recording>
        <recording>
            <artist>Tori Amos</artist>
            <album>Boys For Pele</album>
            <album>To Venus And Back</album>
        </recording>
    </collection>
</sample>

XSLT 2.0

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

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="recording|album">
    <xsl:variable name="count">
      <xsl:number/>
    </xsl:variable>
    <xsl:if test="$count > 1"><x>, </x></xsl:if>
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

XML输出

<sample>
   <collection>
      <recording>
         <artist>Radiohead</artist>
         <album>OK Computer</album>
      </recording>
      <x>, </x>
      <recording>
         <artist>Tori Amos</artist>
         <album>Boys For Pele</album>
         <x>, </x>
         <album>To Venus And Back</album>
      </recording>
   </collection>
</sample>