Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

xsl:sort 1

Status
Not open for further replies.

ma77c

Programmer
Jan 27, 2006
28
CA
I am trying to sort all the of entry elements alphabetically by entryTitle and have the output in XML. My document has the following structure:
Code:
<root>

  <entry>
    <header>
      <index>A1</index>
      <entryTitle>Alligators</entryTitle>
    </header>
    <description>text text text</description>
  </entry>

  <entry>
    ...
  </entry>
</root>

I was thinking something alongs the lines of this, but have had no such luck.
Code:
<xsl:stylesheet version="1.0"
    xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
<xsl:output method="xml" />

<xsl:template match="/">
  <xsl:apply-templates>
    <xsl:sort select="root/entry/header/entryTitle" />
  </xsl:apply-templates>
</xsl:template>

I'm not sure of the best way to go about debugging XSL problems. Any help would be appreciated.
 
It is the correct place to put <xsl:sort>. It can be first child of either <xsl:for-each> or <xsl:apply-templates>. Its normally better to use templates where possible, both for readability and extensibility, although it sometimes makes sense to use a for-each (when a nodeset draws from differently named elements for example)

You've got the right kind of idea with your code, but there's a couple of things you should know.

When you use <xsl:apply-templates> and you don't specify a template, nodes will match against the default template, which will output the value of the node (ie not the XML). To change the behaviour, use a template which matches all nodes and attributes:
Code:
<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
This will output the XML of the node. In your case, you need to override the root node behaviour to sort the entry elements:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:apply-templates select="root">
    <root>
      <xsl:apply-templates select="entry">
        <xsl:sort select="header/entryTitle"/>
      </xsl:apply-templates>
    </root>
  </xsl:apply-templates>
</xsl:stylesheet>

Jon

"I don't regret this, but I both rue and lament it.
 
Thanks for the replies, I appreciate the clarification Jon.

Using your stylesheet originally gave me an error, as I don't believe <xsl:apply-templates> can be a child of <xsl:stylesheet>. This may have just been a typo. I switched it to a <xsl:template> that matches the root element and I am now getting the expected results. Here is the final stylesheet:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="root">
    <root>
      <xsl:apply-templates select="entry">
        <xsl:sort select="header/entryTitle"/>
      </xsl:apply-templates>
    </root>
  </xsl:template>
</xsl:stylesheet>
Thanks for the help.
 
Yep, typo. Thats what I meant :)

Jon

"I don't regret this, but I both rue and lament it.
 
That's excellent. If I've nothing to add, I would leave the thread which contains positive results as such. Nonetheless, if I may, I would like to add something meaningful as well.

[1] xsl:sort applied within apply-templates

[1a] As ma77c noted, it most definitely is indeed a typo of JMC. With that amended, I add this note. It is not need to have <root></root> there. In fact, having it there is an anti-climax, making the template defective! No, it should not be there.

[1b] In the specific overriding template, for a reasonably requirement is to preserve the attributes as well. Otherwise, you can easily verify for a commonly tag with attribute like:

[tt] <root [blue]status="expanded"[/blue]>
<!-- etc etc -->
</root>
[/tt]
you will see that the output would have the status attribute lost.

Hence, combining the remarks [1a] and [1b], the xsl would much improved like this.
[tt]
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="<xsl:eek:utput method="xml" encoding="utf-8" indent="yes" />
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
[blue]<!-- [1b] adding this -->
<xsl:apply-templates select="@*" />[/blue]
<xsl:apply-templates select="entry">
[blue]<!-- [1a] taking out spurious "root" -->[/blue]
<xsl:sort select="header/entryTitle" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
[/tt]
[2] xsl:sort applied within xsl:for-each

As noted, you can actually use sort in two general circumstances, within xsl: apply-templates and within xsl:for-each element. Here, I can show how to realize the same functionality with xsl:sort element so positioned.
[tt]
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="<xsl:eek:utput method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:for-each select="entry">
<xsl:sort select="header/entryTitle"/>
<xsl:apply-templates select="." />
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
[/tt]
I think we thereby sufficiently cover the matter.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top