Link to home
Start Free TrialLog in
Avatar of ramu_src2k
ramu_src2k

asked on

Re-arrange xml elements based on attributes using XSLT

Hi,
I need to re-arrange the below input xml using the "name" attribute through XSLT
<?xml version="1.0" encoding="UTF-16"?>
<codes>
<Entity name="entity4" >
                <somedata1>Raghu</somedata1>
                <somedata2>Hemanth</somedata2>
</Entity>
<Entity name="entity2">
                <somedata1>Shilpa</somedata1>
                <somedata2>Sudha</somedata2>
</Entity>
<Entity name="entity3">
                <somedata1>Ramesh</somedata1>
                <somedata2>Sivakumar</somedata2>
</Entity>
<Entity name="entity1">
                <somedata1>John</somedata1>
                <somedata2>Peter</somedata2>
</Entity>
</codes>


Output xml needs to be:

<?xml version="1.0" encoding="UTF-16"?>
<codes>

<Entity name="entity1">
                <somedata1>John</somedata1>
                <somedata2>Peter</somedata2>
</Entity>
<Entity name="entity2">
                <somedata1>Shilpa</somedata1>
                <somedata2>Sudha</somedata2>
</Entity>
<Entity name="entity3">
                <somedata1>Ramesh</somedata1>
                <somedata2>Sivakumar</somedata2>
</Entity>
<Entity name="entity4" >
                <somedata1>Raghu</somedata1>
                <somedata2>Hemanth</somedata2>
</Entity>
</codes>
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Try this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" encoding="UTF-16"/>
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="codes">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="Entity ">
                <xsl:sort select="substring-after(@name, 'entity')" data-type="number" order="ascending"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Open in new window

Avatar of ramu_src2k
ramu_src2k

ASKER

Can you please let me know how can i handle the scenario if the value of the name attribute are like "cricket" , "football", "golf" , "tennis" etc. where i dont have a number to sort
if you just want to sort alfabetically, use this
                <xsl:sort select="@name" order="ascending"/>
For example my Input XML would be:


<?xml version="1.0" encoding="UTF-16"?>
<codes>
<Entity name="cricket" >
                <somedata1>Raghu</somedata1>
                <somedata2>Hemanth</somedata2>
</Entity>
<Entity name="golf">
                <somedata1>Shilpa</somedata1>
                <somedata2>Sudha</somedata2>
</Entity>
<Entity name="tennis">
                <somedata1>Ramesh</somedata1>
                <somedata2>Sivakumar</somedata2>
</Entity>
<Entity name="football">
                <somedata1>John</somedata1>
                <somedata2>Peter</somedata2>
</Entity>
</codes>


Output should be :


<?xml version="1.0" encoding="UTF-16"?>
<codes>
<Entity name="tennis">
                <somedata1>Ramesh</somedata1>
                <somedata2>Sivakumar</somedata2>
</Entity>
<Entity name="cricket" >
                <somedata1>Raghu</somedata1>
                <somedata2>Hemanth</somedata2>
</Entity>
<Entity name="football">
                <somedata1>John</somedata1>
                <somedata2>Peter</somedata2>
</Entity>
<Entity name="golf">
                <somedata1>Shilpa</somedata1>
                <somedata2>Sudha</somedata2>
</Entity>
</codes>
Actually i tried adding a new attribute called "id" which will rank my element and then use this newly added attribute "id" for sorting. But these thwo things i was not able to do it in a single xslt. I had to use two seperate xslt's to achieve this..Can you please help me to get this done in a single xslt.
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
two XSLTs in a row are not necessarily a bad thing, I do it all the time.
Sometimes two XSLTs in a row are easier to maintain than one complex CXSLT
I tried using the mapping table but the user defined order does not happen.Can you help me regarding this

XML used:

<?xml version="1.0" encoding="UTF-16"?>
<codes>
<Entity name="Cricket" >
                <somedata1>Raghu</somedata1>
                <somedata2>Hemanth</somedata2>
</Entity>
<Entity name="Golf">
                <somedata1>Shilpa</somedata1>
                <somedata2>Sudha</somedata2>
</Entity>
<Entity name="Tennis">
                <somedata1>Ramesh</somedata1>
                <somedata2>Sivakumar</somedata2>
</Entity>
<Entity name="Football">
                <somedata1>John</somedata1>
                <somedata2>Peter</somedata2>
</Entity>
</codes>

XSL used:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:map="gbs:map"  exclude-result-prefixes="map" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" encoding="UTF-16"/>
  <xsl:template match="*">
<codes>
        <xsl:for-each select="Entity">
<xsl:sort select="document('')//map:item[@name=current()]/@order" order="ascending"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
</codes>
    </xsl:template>
    <map:item name="Tennis" order="1"/>
    <map:item name="Cricket" order="2"/>
    <map:item name="Football" order="3"/>
    <map:item name="Golf" order="4"/>
</xsl:stylesheet>
I have got the answer and it meets my requirement. Thanks a lot for your valuable inputs

Here is the solution

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" encoding="UTF-16"/>
<xsl:template match="codes">
<codes>
<xsl:for-each select="Entity">
<xsl:sort
select="(number(@name='Tennis') * 1)
    + (number(@name='Cricket') * 2)
    + (number(@name='Football') * 3)
    + (number(@name='Golf') * 4)"
 order="ascending" data-type="number" />
<xsl:copy-of select="."/>
</xsl:for-each>
</codes>
    </xsl:template>
</xsl:stylesheet>
your mapping statement is wrong
<xsl:sort select="document('')//map:item[@name=current()]/@order" order="ascending"/>
should be
<xsl:sort select="document('')//map:item[@name=current()/@name]/@order" order="ascending"/>
oh, you found something too...
well, that works, I personally find a mapping table more elegant,
but yours will work even when using the document('') function is switched off (it sometimes is for security reasons)
cheers

Geert