XSL Sort on Autogenerated and User entered Titles

Viewed 43

I have a document that contains paragraphs with a mixture of user authored titles and autogenerated titles that I need to have sorted alphabetically for a table of contents.

The data looks like

<theory><text>Stuff goes here</text></theory>
<general><text>More stuff</text></general>
<paragraph><title>First user title</title><text>Lots of stuff</text>   
   <subparagraph><title>Subordinate Paragraph</title><text>The last stuff.</text></subparagraph>
</paragraph>
<paragraph><title>Last user title</title><text>Just a little stuff</text>
</paragraph>

The expected output is

3. First User Title............
2. General Information.........
4. Last User Title.............
3.1 Subordinate Paragraph......
1. Theory of Operation.........

I can't figure out how to get it to sort on the literal values for some paragraphs and the titles when they exist. Right now I have:

<xsl:for-each select="paragraph|theory|general|paragraph/subparagraph">
   <xsl:sort select="name()"/>
   <xsl:sort select="title"/>
   <xsl:apply-templates select="." mode="TOC"/>
</xsl:for-each>

but it outputs as:

2. General................
3. First User Title.......
4. Last User Title........
1. Theory of Operation....
3.1 Subordinate Paragraph.
2 Answers

You could handle this in a more general way by using template rules to compute the sort key:

<xsl:for-each select="paragraph,theory,general">
   <xsl:sort>
      <xsl:apply-templates select="." mode="sort-key"/>
   </xsl:sort>
   <xsl:apply-templates select="." mode="TOC"/>
</xsl:for-each>

<xsl:template match="paragraph" mode="sort-key" as="xs:string">
  <xsl:sequence select="string(title)"/>
</xsl:template>

<xsl:template match="theory|general" mode="sort-key" as="xs:string">
  <xsl:sequence select="string(text)"/>
</xsl:template>

I think you have two issues.

  1. You only want to use the name() if it doesn't have a title
  2. You want to sort case-insensitive

You could change your sort to something like this (XSLT 2.0 or greater):

<xsl:sort select="lower-case((title,name())[1])"/>
Related