Holger
Holger

Reputation: 417

How to move xml element and change attribute value at the same time?

I have a hierachy of posten-elements within a root element, something like

<gliederung>
    <posten id=".." order="1">
        <posten id=".." order"1">
            <posten id=".." order"1">
                 ...
            </posten>
            <posten id="AB" order"2">
                 ...
            </posten>
             ...
        </posten>
        <posten id=".." order"2">
             ...
        </posten>
        <posten id="XY" order"3">
             ...
        </posten>
     ....   
</gliederung>

Each posten has a unique id and a order attribute. Now I need to move element with id "XY" before element with id "AB" and change the order attribute of the moved element "XY" to "1.5".

I managed to move the element with the following script:

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

<xsl:template match="posten[@id='AB']">
     <xsl:copy-of select="../posten[@id='XY']"/>
     <xsl:call-template name="identity"/>
 </xsl:template>

<xsl:template match="posten[@id='XY']"/>

But how to combine the move with changing the order attribute value to "1.5?

I'm missing something obvious I guess ...

Upvotes: 1

Views: 672

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122394

Instead of copy-of, use a template

 <!-- almost-identity template, that does not apply templates to the
      posten[@id='XY'] -->
 <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
        <xsl:apply-templates select="node()[not(self::posten[@id='XY'])]|@*"/>
    </xsl:copy>
 </xsl:template>

<xsl:template match="posten[@id='AB']">
     <!-- apply templates to the XY posten - this will copy it using the
          "identity" template above but will allow the specific template
          for its order attr to fire -->
     <xsl:apply-templates select="../posten[@id='XY']"/>
     <xsl:call-template name="identity"/>
 </xsl:template>

<!-- fix up the order value for XY -->
<xsl:template match="posten[@id='XY']/@order">
  <xsl:attribute name="order">1.5</xsl:attribute>
</xsl:template>

If you're not sure exactly where the XY posten will be relative to the AB one (i.e. will it always be ../posten[@id='XY'] or might it sometimes be ../../), then you could define a

<xsl:key name="postenById" match="posten" use="@id" />

and then replace the <xsl:apply-templates select="../posten[@id='XY']"/> with

<xsl:apply-templates select="key('postenById', 'XY')"/>

Upvotes: 1

Related Questions