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
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>
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:
The identity rule/template copies every node "as-is". There are only two exceptions, explained below.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With