xml - Obtaining Previous element in xslt 1.0 sort order, not dom order -
i have cd list entries not in specific sort order:
<?xml version="1.0" encoding="utf-8"?> <catalog> <cd> <title>empire burlesque</title> <artist>bob dylan</artist> <country>usa</country> <company>columbia</company> <price>10.90</price> <year>1985</year> </cd> <cd> <title>hide heart</title> <artist>bonnie tyler</artist> <country>uk</country> <company>cbs records</company> <price>9.90</price> <year>1988</year> </cd> <cd> <title>greatest hits</title> <artist>dolly parton</artist> <country>usa</country> <company>rca</company> <price>9.90</price> <year>1982</year> </cd> <cd> <title>still got blues</title> <artist>gary moore</artist> <country>uk</country> <company>virgin records</company> <price>10.20</price> <year>1990</year> </cd> </catalog>
i want output list in html, grouped first letter of title. since eluded me how, first ran xml through simple sort:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" xmlns:xs="http://www.w3.org/2001/xmlschema" exclude-result-prefixes="xs" version="1.0"> <xsl:output method="xml" indent="yes" /> <xsl:template match="catalog"> <catalog> <xsl:apply-templates select="cd"><xsl:sort select="title"/></xsl:apply-templates> </catalog> </xsl:template> <xsl:template match="cd"><xsl:copy-of select="."/></xsl:template> </xsl:stylesheet>
which enables me use preceding-sibling
check change in title letter. resulting sheet looks this:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:template match="/"> <html> <head> <title> booklist books <xsl:value-of select="count(/catalog/cd)"/> </title> <style type="text/css"> table.main {width : 100%} table.main td {padding : 2px; border-bottom : 1px solid gray} th {text-align : left} tr.header {background-color : #9acd32} table.bar {border: 1px solid gray; background-color #cacaca} table.bar td {border-left : 1px solid gray; padding : 4px; margin : 2px; font-size : x-large} tr.firstbook {background-color : #cacaca} td.firstbook {font-size : xx-large} td.firstbook a.up {text-decoration: none; font-size : normal} </style> </head> <body> <xsl:apply-templates mode="header"/> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="catalog"> <table class="main"> <tr class="header"> <th> title </th> <th> artist </th> <th> country </th> <th> company </th> <th> price </th> <th> year </th> </tr> <xsl:apply-templates select="cd"> <xsl:sort select="title"/> </xsl:apply-templates> </table> </xsl:template> <xsl:template match="cd"> <xsl:variable name="firstletter" select="substring(title,1,1)"/> <xsl:variable name="oldletter" select="substring(preceding-sibling::*[1]/title,1,1)"/> <xsl:if test="not($firstletter=$oldletter)"> <tr class="firstbook"> <td class="firstbook" colspan="5"> <a name="{$firstletter}"> <xsl:value-of select="$firstletter"/> </a> </td> <td class="firstbook"> <a class="up" href="#">⬆</a> </td> </tr> </xsl:if> <tr> <td> <xsl:value-of select="title"/> </td> <td> <xsl:value-of select="artist"/> </td> <td> <xsl:value-of select="country"/> </td> <td> <xsl:value-of select="company"/> </td> <td> <xsl:value-of select="price"/> </td> <td> <xsl:value-of select="year"/> </td> </tr> </xsl:template> <!-- header link handling --> <xsl:template match="catalog" mode="header"> <table class="bar"> <tr> <xsl:apply-templates mode="header" select="cd[not(substring(title,1,1)=substring(preceding-sibling::*[1]/title,1,1))]"> <xsl:sort select="title"/> </xsl:apply-templates> </tr> </table> </xsl:template> <xsl:template mode="header" match="cd"> <xsl:variable name="firstletter" select="substring(title,1,1)"/> <td> <a href="#{$firstletter}"> <xsl:value-of select="$firstletter"/> </a> </td> </xsl:template> </xsl:stylesheet>
the key part comparison: not(substring(title,1,1)=substring(preceding-sibling::*[1]/title,1,1))
looks @ dom , not result of sort operation.
what i'm looking way in xslt-1.0 combine effect of 2 transformations, have 1 stylesheet, unsorted input list , result looks 2 stylesheets produce:
how do that?
you can sort first variable (which in xslt result tree fragment), can use extension function exsl:node-set
convert result tree fragment node-set processed further existing code.
so need 2 changes, template catalog
has be
<xsl:template match="catalog"> <table class="main"> <tr class="header"> <th> title </th> <th> artist </th> <th> country </th> <th> company </th> <th> price </th> <th> year </th> </tr> <xsl:variable name="sorted-cds"> <xsl:for-each select="cd"> <xsl:sort select="title"/> <xsl:copy-of select="."/> </xsl:for-each> </xsl:variable> <xsl:apply-templates select="exsl:node-set($sorted-cds)/cd"/> </table> </xsl:template>
and stylesheet root has declare exsl
namespace:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl">
note not xslt processors support exsl:node-set
while support @ least similar extension function in proprietary namespace. assuming want use microsoft's msxml (for instance inside of internet explorer), need use <xsl:apply-templates select="ms:node-set($sorted-cds)/cd"/>
,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform" xmlns:ms="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="ms">
Comments
Post a Comment