XSLT conditional sorting
I have following XML:
<Users>
<User Id="1">
<Name>abc</Name>
<LastName>d</LastName>
</User>
<User Id="2">
<Name></Name>
<LastName>ab</LastName>
</User>
<User Id="3">
<Name>a</Name>
<LastName>efg</LastName>
</User>
</Users>
Now I sort users using following template:
<xsl:template match="Users"&开发者_Go百科gt;
<Users>
<xsl:for-each select="User">
<xsl:sort select="Name"/>
<xsl:sort select="LastName"/>
<User>
<xsl:attribute name="Id">
<xsl:value-of select="attribute::Id"/>
</xsl:attribute>
<Name>
<xsl:value-of select="Name"/>
</Name>
<LastName>
<xsl:value-of select="LastName"/>
</LastName>
</User>
</xsl:for-each>
</Users>
</xsl:template>
But I need sorting, which satisfies following condition: Sort by Name. If Name is empty or null, I need to sort by LastName. So in produced XML I need following ordering: User3, User2, User1.
Any help is appreciated.
P.S.: I use ASP.NET 3.5
I would use first the identity transformation, and then apply the sorting to the union of the elements (excluding those whose Name
is empty)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Users">
<xsl:copy>
<xsl:apply-templates select="User">
<xsl:sort select="Name[.!='']|LastName"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied on the input shown in the question, we obtain:
<Users>
<User Id="3">
<Name>a</Name>
<LastName>efg</LastName>
</User>
<User Id="2">
<Name/>
<LastName>ab</LastName>
</User>
<User Id="1">
<Name>abc</Name>
<LastName>d</LastName>
</User>
</Users>
You can select the Name
and LastName
elements with predicate filters that use normalize-space()
to filter out the empty ones, use the union operator |
to combine them and group them with the perenthesis(what would create a sequence in XSLT/XPath 2.0). Then, select the first one in the grouping to use for the sort.
Also, rather than re-construct the <Users>
element, you can just use <xsl:copy-of>
.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:template match="Users">
<Users>
<xsl:for-each select="User">
<xsl:sort select="(Name[normalize-space()]|LastName[normalize-space()])[1]"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</Users>
</xsl:template>
</xsl:stylesheet>
精彩评论