user3793430
user3793430

Reputation:

XSLT add element at specific position

i want to transform an XML File as following:

This is my main original XML file:

<persons>
  <child name="child1"></child>
  <adult name="adult1"></adult>
  <child name="child2"></child>
  <child name="child3"></child>
  <COMMAND type="INSERT" file="otherchilds.xml"/>
  <adult name="adult2"></adult>
  <adult name="adult3"></adult>
</persons>

and there could be XML Files which should be imported by the XSLT whenever there is a tag COMMAND with an attribute "INSERT" in the main XML File. I have to use XSLT 1.0

otherchilds.xml:

<childs>
  <child name="child4"></child>
  <child name="child5"></child>
<childs>

the two XML Files should transform to one XML File:

<root>
  <children>
    <child1></child1>
    <child2></child2>
    <child3></child3>
    <child4></child4>
    <child5></child5>
  </children>
  <persons>
    <child name="child1"></child>
    <adult name="adult1"></adult>
    <child name="child2"></child>
    <child name="child3"></child>
    <child name="child4"></child>
    <child name="child5"></child>
    <adult name="adult2"></adult>
    <adult name="adult3"></adult>
  </persons>
</root>

So whenever a child is found, it should be added in the children element.

Upvotes: 0

Views: 1720

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167471

Using the XSLT code

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output indent="yes"/>

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

    <xsl:template match="persons">
        <children>
            <xsl:apply-templates select="child" mode="el"/>
        </children>
        <xsl:call-template name="identity"/>
    </xsl:template>

    <xsl:template match="child" mode="el">
        <xsl:element name="{@name}">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
</xsl:transform>

the input

<persons>
  <child name="child1"></child>
  <adult name="adult1"></adult>
  <child name="child2"></child>
  <child name="child3"></child>
  <adult name="adult2"></adult>
  <adult name="adult3"></adult>
</persons>

is transformed to the result

<?xml version="1.0" encoding="utf-8"?>
<children>
   <child1/>
   <child2/>
   <child3/>
</children>
<persons>

   <child name="child1"/>

   <adult name="adult1"/>

   <child name="child2"/>

   <child name="child3"/>

   <adult name="adult2"/>

   <adult name="adult3"/>

</persons>

Online sample at http://xsltransform.net/bnnZVR.

As for the changed requirements where there are additional elements to be loaded from a second file, the code can be changed to

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output 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="persons">
        <children>
            <xsl:apply-templates select="child | document(COMMAND[@type = 'INSERT']/@file)//child" mode="el"/>
        </children>
        <xsl:call-template name="identity"/>
    </xsl:template>

    <xsl:template match="child" mode="el">
        <xsl:element name="{@name}">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="COMMAND[@type = 'INSERT'][@file]">
      <xsl:apply-templates select="document(@file)//child"/>
    </xsl:template>

</xsl:transform>

to handle that particular case.

Upvotes: 1

Related Questions