XSLT to bring simple tags above complex tags in XML

Question

I have an XML where few simple tags appear after nested tags. I was trying to figure out a way if it is possible to bring all simple tags above the nested tags. Example

<Country>
<row>
    <CountryId>1</CountryId>
    <State>
        <StateId>2</StateId>
        <StateName>Karnataka</StateName>
    </State>
    <CountryName>India</CountryName>
</row>
<row>
    <CountryId>3</CountryId>
    <State>
        <StateId>4</StateId>
        <StateName>Sydney</StateName>
    </State>
    <CountryName>Australia</CountryName>
</row>

Transformed XML that is expected is :

<Country>
<row>
    <CountryId>1</CountryId>
    <CountryName>India</CountryName>
    <State>
        <StateId>2</StateId>
        <StateName>Karnataka</StateName>
    </State>
</row>
<row>
    <CountryId>3</CountryId>
    <CountryName>Australia</CountryName>
    <State>
        <StateId>4</StateId>
        <StateName>Sydney</StateName>
    </State>
</row>

This XML can be any generic XML with n levels and so I want to hardcode any of the tags within the XSLT. Ths XSLT should be able to work on a huge XML to bring all the simple tags above nested tags. Is it advisable to do this using XSLT? What are the other options available?


Show source
| xml   | xslt   2016-11-21 09:11 2 Answers

Answers ( 2 )

  1. 2016-11-21 11:11

    If you don't have mixed contents (i.e. elements having both text and element contents) you can simply write a template for those elements having element content match="*[*]" which processes the child elements without child elements first before the child elements with child elements. Using XSLT 2.0 that can be achieved with

    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
        <xsl:output indent="yes"/>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="*[*]">
            <xsl:copy>
                <xsl:apply-templates select="@*, *[not(*)], *[*]"/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:transform>
    

    online at http://xsltransform.net/pPJ8LVo.

    If you only have an XSLT 1.0 processor then you have to break up the <xsl:apply-templates select="@*, *[not(*)], *[*]"/> into

    <xsl:apply-templates select="@* | *[not(*)]"/>
    <xsl:apply-templates select="*[*]"/>
    
  2. 2016-11-21 13:11

    I would do simply:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
                <xsl:sort select="boolean(*)" data-type="text" order="ascending"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    
◀ Go back