Arunmu
Arunmu

Reputation: 6901

XSLT conditionally processing sibling attributes

I have an XML element like this:

<element addr="::" value="1">

Now, I want to change the value of value to "0", if addr is "::".
A logical solution for me would be something like this:

<xsl:template match="element/@*">
    <xsl:if test="@addr = '::'">
        <xsl:message>Matched</xsl:message>
        <xsl:attribute name="value">0</xsl:attribute>
    </xsl:if>
    <xsl:copy>
        <xsl:apply-templates select="node()" />
    </xsl:copy>
</xsl:template>

But this does not seem to work.
How can I correct this?

Upvotes: 1

Views: 1092

Answers (2)

Michael Kay
Michael Kay

Reputation: 163418

Use an identity template that copies everything:

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

and override it with a template rule that modifies what you want to modify:

<xsl:template match="@value[../@addr = '::']">
  <xsl:attribute name="value">0</xsl:attribute>
</xsl:template>

Upvotes: 4

michael.hor257k
michael.hor257k

Reputation: 117043

The reason why your attempt doesn't work is that your template matches "element/@*" i.e. each and every attribute of element. Within that context, the condition <xsl:if test="@addr = '::'"> will never return true, because neither attribute has (or can have) a child attribute named addr.

To modify only the value attribute, make your template match it expressly, either as:

<xsl:template match="@value">

or - if you have other elements with an attribute named value and you want to be sure to exclude those - as:

<xsl:template match="element/@value">

Then you can replace it (or not) conditionally by:

<xsl:choose>
    <xsl:when test="../@addr = '::'">
        <xsl:attribute name="value">0</xsl:attribute>
    </xsl:when>
    <xsl:otherwise>
        <xsl:copy/>
    </xsl:otherwise>
</xsl:choose>

Alternatively, you could do it this way:

<xsl:template match="element[@value and @addr = '::']">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:attribute name="value">0</xsl:attribute>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

i.e. match the element that has the attribute that needs changing and overwrite that attribute.

Note that we are assuming here that you also have an identity transform template in place.

Upvotes: 1

Related Questions