XSLT sorting issue

2019-09-19 18:38发布

I have an xml like:

<PersonList>            
<Person>
    <Name>Smith</Name>
    <Role>5</Role>
</Person>
 <Person>
    <Name>Star</Name>
    <Role>3</Role>
</Person>
<Person>
    <Name>Wars</Name>
    <Role>1</Role>
</Person>    
</PersonList> 

In xslt I want to sort in such a way that if there is a Role 1 this should be the first Person in the list. The rest of the Persons should be sorted alphabetically by Name.

Thanks in advance.

标签: xml sorting xslt
2条回答
\"骚年 ilove
2楼-- · 2019-09-19 19:17

A simpler and shorter solution -- no numeric sorting is really necessary:

<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()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="*">
        <xsl:sort select="not(Role=1)"/>
        <xsl:sort select="Name"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Explanation:

  1. When Boolean values are sorted, false() comes before true().

  2. Multi-key sorting

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-09-19 19:18

Try it this way:

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="*"/>

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

<xsl:template match="/PersonList">
    <xsl:copy>
        <xsl:apply-templates select="Person">
            <xsl:sort select="number(Role=1)" data-type="number" order="descending"/>
            <xsl:sort select="Name" data-type="text" order="ascending"/>
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Or, if you prefer:

...
<xsl:template match="/PersonList">
    <xsl:copy>
        <xsl:apply-templates select="Person[Role=1]"/>
        <xsl:apply-templates select="Person[not(Role=1)]">
            <xsl:sort select="Name" data-type="text" order="ascending"/>
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>
...

Additional Explanation:

The expression Role=1 returns a Boolean result. However, XSLT 1.0's xsl:sort instruction can only sort by text or by number. Therefore the result must be converted to either a string or a number, before it can be used for sorting.

I prefer to convert the Boolean to a number, because then the intended order is easier to understand when reading the code.

For the same reason, I prefer to state the data-type and order explicitly, even when they are the default values of "text" and "ascending" respectively.

In any case, if one prefers to sort as text, the required process is the same in terms of complexity1:

<xsl:sort select="not(Role=1)"/>

is just a short-hand version of:

<xsl:sort select="string(not(Role=1))" data-type="text" order="ascending"/>

which is the same thing as:

<xsl:sort select="string(Role=1)" data-type="text" order="descending"/>

The only difference between these three is in code readability.


(1) One could argue that sorting numerically will be more efficient than sorting alphabetically, but I don't know that for a fact, so I won't.


Note:
These differences are minor and largely a matter of personal preference.

查看更多
登录 后发表回答