Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move an xml element into another element with xslt

I have an XML that looks like this

<executionPlan name="Test" >
<paramList>
    <param name="param1" default=""/>
</paramList>

<varList>
    <var name="bla" default=":[param1]"/>
</varList>

<simpleSteps limitToHostSet="bla">
    <execNative>
        <exec cmd="/bin/sh"/>
    </execNative>
</simpleSteps>

and I need to transform it to look like this:

<executionPlan name="Test" >
<paramList>
    <param name="param1" default=""/>
</paramList>

<simpleSteps limitToHostSet="bla">
    <varList>
        <var name="bla" default=":[param1]"/>
    </varList>
    <execNative>
        <exec cmd="/bin/sh"/>
    </execNative>
</simpleSteps>

As you can see the varList element needs to be nested inside the simpleSteps element immediately behind the opening tag. There may be other varList elements inside simpleSteps which must not be changed.

Any ideas how to achieve that with XSLT? I am a newby to XSLT and tried the whole day in vain... Any help would really be appreciated.

Lutz

like image 706
Lutz Avatar asked Apr 14 '11 15:04

Lutz


2 Answers

The following style sheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="varList[following-sibling::*[1][self::simpleSteps]]" />
    <xsl:template match="simpleSteps">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:copy-of select="preceding-sibling::varList[1]" />
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

On this input:

<executionPlan name="Test">
    <paramList>
        <param name="param1" default="" />
    </paramList>
    <varList>
        <var name="bla" default=":[param1]" />
    </varList>
    <varList>
        <var name="bla2" default=":[param2]" />
    </varList>
    <simpleSteps limitToHostSet="bla">
        <execNative>
            <exec cmd="/bin/sh" />
        </execNative>
    </simpleSteps>
</executionPlan>

Produces:

<executionPlan name="Test">
    <paramList>
        <param name="param1" default="" />
    </paramList>
    <varList>
        <var name="bla" default=":[param1]" />
    </varList>
    <simpleSteps limitToHostSet="bla">
        <varList>
            <var name="bla2" default=":[param2]" />
        </varList>
        <execNative>
            <exec cmd="/bin/sh" />
        </execNative>
    </simpleSteps>
</executionPlan>

Edit: Only the immediately preceding varList is moved into its associated simpleSteps. All other varList elements are copied through unchanged.

It's suddenly not clear to me whether this is the desired behavior, or if there may be multiple varList elements already inside the simpleSteps element that should be unchanged. See my original solution for that case:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="varList" />
    <xsl:template match="simpleSteps">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:copy-of select="../varList" />
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

On this input:

<executionPlan name="Test">
    <paramList>
        <param name="param1" default="" />
    </paramList>
    <varList>
        <var name="bla" default=":[param1]" />
    </varList>
    <simpleSteps limitToHostSet="bla">
        <varList>
            <var name="bla7" default=":[param7]" />
        </varList>
        <execNative>
            <exec cmd="/bin/sh" />
        </execNative>
    </simpleSteps>
</executionPlan>

Produces:

<executionPlan name="Test">
    <paramList>
        <param name="param1" default="" />
    </paramList>
    <simpleSteps limitToHostSet="bla">
        <varList>
            <var name="bla" default=":[param1]" />
        </varList>
        <varList>
            <var name="bla7" default=":[param7]" />
        </varList>
        <execNative>
            <exec cmd="/bin/sh" />
        </execNative>
    </simpleSteps>
</executionPlan>
like image 76
Wayne Avatar answered Oct 20 '22 00:10

Wayne


This is a simpler and shorter solution:

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

 <xsl:template match="simpleSteps/*[1]">
  <xsl:copy-of select="/*/varList[1]"/>
  <xsl:call-template name="identity"/>
 </xsl:template>

 <xsl:template match="/*/varList[1]"/>
</xsl:stylesheet>

when applied on the provided XML document:

<executionPlan name="Test" >
    <paramList>
        <param name="param1" default=""/>
    </paramList>
    <varList>
        <var name="bla" default=":[param1]"/>
    </varList>
    <simpleSteps limitToHostSet="bla">
        <execNative>
            <exec cmd="/bin/sh"/>
        </execNative>
    </simpleSteps>
</executionPlan>

exactly the wanted, correct result is produced:

<executionPlan name="Test">
   <paramList>
      <param name="param1" default=""/>
   </paramList>
   <simpleSteps limitToHostSet="bla">
      <varList>
         <var name="bla" default=":[param1]"/>
      </varList>
      <execNative>
         <exec cmd="/bin/sh"/>
      </execNative>
   </simpleSteps>
</executionPlan>

Explanation:

  1. The identity rule/template copies every node "as-is". There are only two exceptions, explained below.

  2. The overriding template that matches only the first varList child of the top element has no body -- this effectively annuls the copying action of the identity template for this element.

  3. The overriding template that matches the first element child of simpleSteps does two things: a) it copies the wanted varList (child of the top element) element and then b) it calls the identity template to copy itself to the output.

like image 37
Dimitre Novatchev Avatar answered Oct 19 '22 23:10

Dimitre Novatchev