Move an xml element into another element with xslt

旧时模样 提交于 2019-12-23 11:59:09

问题


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


回答1:


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>



回答2:


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.



来源:https://stackoverflow.com/questions/5665616/move-an-xml-element-into-another-element-with-xslt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!