用 XSLT 覆盖属性

Overwrite attribute with XSLT

我有 XML:

<doc>
    <p id="123" sec="abc"></p>
</doc>

使用 XSLT,我需要:

1) 添加新属性 name,值为 'myname'

2) 复制相同的 sec

3) 将 id 属性覆盖为新值

我为此编写了以下 XSLT,

<xsl:template match="p">
    <p name="myname" id="999">
        <xsl:apply-templates select="node()|@*"/>
    </p>
</xsl:template>

它给了我以下结果:

<doc>
   <p name="myname" id="123" sec="abc"></p>
</doc>

期望的结果是:

<doc>
   <p name="myname" id="999" sec="abc"></p>
</doc>

它似乎没有覆盖 id 属性值。如何从 XSLT 覆盖这个值?

我目前无法自己尝试...请尽量不要复制 'id' 属性,因为这会否决 xslt 中的 'id' 属性。

<xsl:template match="p">
        <p name="myname" id="999">
            <xsl:apply-templates select="node()|@*[local-name() != 'id']"/>
        </p>
</xsl:template>

更改模板

<xsl:template match="p">
        <p name="myname" id="999">
            <xsl:apply-templates select="node()|@*"/>
        </p>
</xsl:template>

<xsl:template match="p">
        <p name="myname" id="999">
            <xsl:apply-templates select="@* except @id, node()"/>
        </p>
</xsl:template>

或者为 id 属性写一个模板:

<xsl:template match="p">
  <p name="myname">
     <xsl:apply-templates select="@* , node()"/>
  </p>
</xsl:template>

<xsl:template match="p/@id">
  <xsl:attribute name="id" select="999"/>
</xsl:template>

或者简单地说:

<xsl:template match="p">
    <p name="myname" id="999" sec="{@sec}"/>
</xsl:template>

--

如果 p 元素可以包含其他节点(与显示的示例不同),请使用:

<xsl:template match="p">
    <p name="myname" id="999" sec="{@sec}">
        <xsl:apply-templates/>
    </p>    
</xsl:template>

关键是如果你给一个元素添加多个同名的属性,最后一个会胜出。您的显式 id="999" 被认为在您使用 xsl:apply-templates 调用复制的属性之前,因此它没有任何效果。

有几种解决方法。您可以避免将模板应用于 @id 属性(在应用模板中使用 select);您可以为 @id 属性设置一个不会导致它被复制的模板规则;或者您可以在执行应用模板后添加 id="999" 属性,方法是在 xsl:apply-模板指令之后出现 xsl:attribute 指令。