VEnglisch
VEnglisch

Reputation: 13

XSLT Matching a template to change an attribute just once

I have a document with a specific attribute (myId) for which the value needs to be updated whenever its value is zero. The document looks like this

<?xml version="1.0" encoding="UTF-8"?><Summary>
 <Section myId="0">
  <Section myId="0">
   <Para>...</Para>
  </Section>
  <Section myId="5">
   <Para>...</Para>
  </Section>
 </Section>
</Summary>

I am using a template to match the attribute myId in order to set it to a unique ID passed from a calling program but I only want to match one of the attributes in the document. Any additional attributes with a value of zero will be updated by passing a different ID. My template I'm using looks like this:

 <xsl:template        match  = '@myId[.="0"]'>
  <xsl:attribute name = "{name()}">
   <xsl:value-of select = "$addValue"/>
  </xsl:attribute>
 </xsl:template>

The value addValue is a global parameter passed from the calling program. I've searched for an answer for a good part of the day but I'm unable to have this template be applied only just once. The output replaces both myId values with the content of addValue. I've tried to match with '@myId[."0"][1]' and I've tried to use the position() function to to match but my template is always applied to all myId attributes that are zero.

Is it possible to apply a matching template only once?

Upvotes: 1

Views: 1657

Answers (1)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

Is it possible to apply a matching template only once?

Yes:

  1. Whether a template is applied or not depends on the xsl:apply-templates that causes the template to be selected for execution.

  2. Additionaly, the match pattern can be specified in a way that guarantees that the template matches only one specific node in the document.

Here is what you can do:

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

 <xsl:param name="pNewIdValue" select="9999"/>


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

 <xsl:template match=
 "Section
   [@myId = 0
  and
    not((preceding::Section | ancestor::Section)
                 [@myId = 0]
       )
   ]/@myId">
  <xsl:attribute name="myId"><xsl:value-of select="$pNewIdValue"/></xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied to the provided XML document:

<Summary>
    <Section myId="0">
        <Section myId="0">
            <Para>...</Para>
        </Section>
        <Section myId="5">
            <Para>...</Para>
        </Section>
    </Section>
</Summary>

the wanted, correct result is produced:

<Summary>
   <Section myId="9999">
      <Section myId="0">
         <Para>...</Para>
      </Section>
      <Section myId="5">
         <Para>...</Para>
      </Section>
   </Section>
</Summary>

Upvotes: 1

Related Questions