XSLT sort by attribute and attribute value (with exclude)

3.5k views Asked by At

I have a following XML code:

<module>
    <component>
        <output>
        <output-test>
        <exclude-output/>
        <orderEntry type="specific"/>
        <orderEntry type="other"/>
        <orderEntry type="module" module-name="module1"/>
        <orderEntry type="module" module-name="module3"/>
        <orderEntry type="module" module-name="module2"/>
        <orderEntry type="library" name="library1"/>
        <orderEntry type="library" name="library3"/>
        <orderEntry type="module" module-name="module4"/>
        <orderEntry type="library" name="library2"/>
    </component>
</module>

I would like to have the output:

<module>
    <component>
        <output>
        <output-test>
        <exclude-output/>
        <orderEntry type="specific"/>
        <orderEntry type="other"/>
        <orderEntry type="module" module-name="module1"/>
        <orderEntry type="module" module-name="module2"/>
        <orderEntry type="module" module-name="module3"/>
        <orderEntry type="module" module-name="module4"/>
        <orderEntry type="library" name="library1"/>
        <orderEntry type="library" name="library2"/>
        <orderEntry type="library" name="library3"/>
    </component>
</module>
1

There are 1 answers

1
Tim C On BEST ANSWER

I am not entirely sure if this meets your requirements, but you could explicitly select the ones you want to come first in a separate xsl:apply-templates, then select the others with a sort on their type attribute

<xsl:apply-templates select="orderEntry[@type='specific' or @type='other']" />
<xsl:apply-templates select="orderEntry[@type!='specific' and @type!='other']">
   <xsl:sort select="@type" order="descending" />
   <xsl:sort select="@module-name" order="ascending" />
   <xsl:sort select="@name" order="ascending" />
</xsl:apply-templates>

Looking at the current expected output, it seems all the type attributes are in descending order, so you could simplify it to this

<xsl:apply-templates select="orderEntry">
    <xsl:sort select="@type" order="descending" />
    <xsl:sort select="@module-name" order="ascending" />
    <xsl:sort select="@name" order="ascending" />
</xsl:apply-templates>

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*" />
    <xsl:template match="component">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[not(self::orderEntry)]"/>
            <xsl:apply-templates select="orderEntry">
                <xsl:sort select="@type" order="descending" />
                <xsl:sort select="@module-name" order="ascending" />
                <xsl:sort select="@name" order="ascending" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

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

Note that, if you actually wanted to sort on type attribute that were not in alphabetical order, you could do something like this:

<xsl:apply-templates select="orderEntry">
    <xsl:sort select="string-length(substring-before('specific,other,module,library',@type))" order="ascending" />
    <xsl:sort select="@module-name" order="ascending" />
    <xsl:sort select="@name" order="ascending" />
</xsl:apply-templates>