Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrap multiple optional XML elements into a new wrapper XML element using XSLT

Tags:

xml

xslt

I have an XML file which looks like the following:

<a>
  ......
  <b>
    <c>
      <c1>some text</c1>
    </c>
    <d>
      <d1>some more text</d1>
      <d1>even more text</d1>
    </d>
    <e>
      <e1>some more text</e1>
      <e1>even more text</e1>
    </e>
  </b>
</a>

And I want to wrap elements <d> and <e> into a <wrapper> element, so I can have something like the following:

<a>
  ......
  <b>
    <c>
      <c1>some text</c1>
    </c>
    <wrapper>
      <d>
        <d1>some more text</d1>
        <d1>even more text</d1>
      </d>
      <e>
        <e1>some more text</e1>
        <e1>even more text</e1>
        <e2>even more</e2>
      </e>
    </wrapper>
  </b>
  ......
</a>

One of the problems that I'm facing (apart from the fact that I'm new to XSLT) is that both <d> and <e> are optional.

How can I do this?

like image 560
Alex Ntousias Avatar asked May 08 '26 18:05

Alex Ntousias


1 Answers

This transformation:

<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="b/*[self::d or self::e][1]">
  <wrapper>
    <xsl:apply-templates mode="copy" select=
    ". | following-sibling::*[self::d or self::e]"/>
  </wrapper>
 </xsl:template>

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

 <xsl:template match="b/*[self::d or self::e][position()>1]"/>
</xsl:stylesheet>

when applied on the provided XML document:

<a>   ......   
    <b>
        <c>
            <c1>some text</c1>
        </c>
        <d>
            <d1>some more text</d1>
            <d1>even more text</d1>
        </d>
        <e>
            <e1>some more text</e1>
            <e1>even more text</e1>
        </e>
    </b>
</a>

produces the wanted result:

<a>   ......   
    <b>
        <c>
            <c1>some text</c1>
        </c>
        <wrapper>
            <d>
                <d1>some more text</d1>
                <d1>even more text</d1>
            </d>
            <e>
                <e1>some more text</e1>
                <e1>even more text</e1>
            </e>
        </wrapper>
    </b>
</a>

Note: Overriden identity rule + modes.

like image 81
Dimitre Novatchev Avatar answered May 11 '26 15:05

Dimitre Novatchev