XSLT multiple output: copy selected nodes and the rest too

293 Views Asked by At

I have the following input xml:

<?xml version="1.0" encoding="UTF-8"?>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <extras>
      <extra>
         <model>Z1</model>
      </extra>
      <extra>
         <model>Z2</model>
      </extra>
      <extra>
         <model>X</model>
      </extra>
   </extras>
   <color>red</color>
   <tire>goodyear</tire>
</car>

I'm trying to generate one output xml for each <extra>..</extra> element in a way that the resulting xmls should contain all the elements from the input but only one particular <extra> element. I.e, the resulting outputs should be:

extra1.xml:

<?xml version="1.0" encoding="UTF-8"?>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <extras>
      <extra>
         <model>Z1</model>
      </extra>
   </extras>
   <color>red</color>
   <tire>goodyear</tire>
</car>

extra2.xml:

<?xml version="1.0" encoding="UTF-8"?>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <extras>
      <extra>
         <model>Z2</model>
      </extra>
   </extras>
   <color>red</color>
   <tire>goodyear</tire>
</car>

extra3.xml:

<?xml version="1.0" encoding="UTF-8"?>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <extras>
      <extra>
         <model>X</model>
      </extra>
   </extras>
   <color>red</color>
   <tire>goodyear</tire>
</car>

I'm using from XSLT 2.0 Saxon-HE 9.6.0-5.

My xslt looks like as follows:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />
    <xsl:strip-space elements="*" />

    <xsl:template name="full">
        <xsl:param name="enode" />
        <xsl:for-each select="/">
            <xsl:if test="*[not($enode = current())]">
                <xsl:copy-of select="current()" />
            </xsl:if>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="extras">
        <xsl:for-each select="//extra">
            <xsl:result-document href="extra{position()}.xml">
                <xsl:call-template name="full">
                    <xsl:with-param name="enode" select="current()" />
                </xsl:call-template>
                <extras>
                    <xsl:copy-of select="current()" />
                </extras>
            </xsl:result-document>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

I'm able to generate the output xmls which only contain one <extra> element, but how to copy the rest of the elements from the input (i.e: all elements except children of <extras>?) I tried to create a template "full" which loops through all the nodes and copies only those that are different than the node I'm passing as a parameter (enode), but no success.
Is there any (other) way to obtain the desired result?
If the order of the elements in the resulting xml is different what I provided is not a problem, I can rearrange them in a second pass.

1

There are 1 best solutions below

0
On BEST ANSWER

Couldn't it be simply:

XSLT 2.0

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />

<xsl:template match="/car">
    <xsl:for-each select="extras/extra">
        <xsl:result-document href="extra{position()}.xml">
            <car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <extras>
                     <xsl:copy-of select="*"/>
                </extras>
                <xsl:copy-of select="../../(* except extras)"/>
            </car>
        </xsl:result-document>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>