Sort C# XML Documentation Output by Class Name

I'm trying to write an XSLT for the XML documentation file that gets output by Visual Studio 2005 when you build a project. The class names are in a seemingly random order and I'd like them and their methods/properties/fields to be sorted.

Below is my current XSL file, but the output is really ugly as I'm not a designer or anything. I've tried using <xsl:sort select="@name"/> throughout the XSL file, but it only sorts the methods/properties/fields and not the classes. The XSL file I'm using came from CodeProject (http://www.codeproject.com/KB/XML/XMLDocStylesheet.aspx).

If someone can provide me with a working XSL file that would be great. Normally, I would try to fix what I've got but as I've said, it's ugly anyways. Besides, I would like to have a complete (good) example to look at for future XSL files I'll have to make.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<!-- DOCUMENT TEMPLATE -->
<!-- Format the whole document as a valid HTML document -->
<xsl:template match="/">
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="tDocc.css" />
  </head>
  <body>
    <xsl:apply-templates select="//assembly">
      <xsl:sort select="member>@name" data-type="text" order="ascending"/>
    </xsl:apply-templates>
  </body>
</html>
</xsl:template>
 
<!-- ASSEMBLY TEMPLATE -->
<!-- For each Assembly, display its name and then its member types -->
<xsl:template match="assembly">
<h1><xsl:value-of select="name"/></h1>
  <xsl:apply-templates select="//member[contains(@name,'T:')]">
    <xsl:sort select="@name" data-type="text" order="ascending"/>
  </xsl:apply-templates>
</xsl:template>
 
<!-- TYPE TEMPLATE -->
<!-- Loop through member types and display their properties and methods -->
<xsl:template match="//member[contains(@name,'T:')]">
 
  <!-- Two variables to make code easier to read -->
  <!-- A variable for the name of this type -->
  <xsl:variable name="MemberName"
                 select="substring-after(@name, '.')"/>
 
  <!-- Get the type's fully qualified name without the T: prefix -->
  <xsl:variable name="FullMemberName"
                 select="substring-after(@name, ':')"/>
  
  <!-- Display the type's name and information -->
  <h2><xsl:value-of select="$MemberName"/></h2>  
 
  <xsl:apply-templates>
    <xsl:sort select="@name" data-type="text" order="ascending"/>
  </xsl:apply-templates>
 
  <!-- If this type has public fields, display them -->
  <xsl:if test="//member[contains(@name,concat('F:',$FullMemberName))]">
   <h3>Fields</h3>
   <div class="members">
 
      <xsl:for-each select="//member[contains(@name,concat('F:',$FullMemberName))]">
        <xsl:sort select="@name" data-type="text" order="ascending"/>
        <h4><xsl:value-of select="substring-after(@name, concat('F:',$FullMemberName,'.'))"/></h4>
          <xsl:apply-templates>
    	    <xsl:sort select="@name" data-type="text" order="ascending"/>
 		  </xsl:apply-templates>
      </xsl:for-each>
    </div>
  </xsl:if>
 
  <!-- If this type has properties, display them -->
  <xsl:if test="//member[contains(@name,concat('P:',$FullMemberName))]">
  <h3>Properties</h3>
  <div class="members">
 
      <xsl:for-each select="//member[contains(@name,concat('P:',$FullMemberName))]">
        <xsl:sort select="@name" data-type="text" order="ascending"/>
        <h4><xsl:value-of select="substring-after(@name, concat('P:',$FullMemberName,'.'))"/></h4>
          <xsl:apply-templates>
    	    <xsl:sort select="@name" data-type="text" order="ascending"/>
 		  </xsl:apply-templates>
      </xsl:for-each>
  </div>
  </xsl:if>
   
  <!-- If this type has methods, display them -->
  <xsl:if test="//member[contains(@name,concat('M:',$FullMemberName))]">
  <h3>Methods</h3>
  <div class="members">
   
    <xsl:for-each select="//member[contains(@name,concat('M:',$FullMemberName))]">
        <xsl:sort select="@name" data-type="text" order="ascending"/>
        <!-- If this is a constructor, display the type name 
            (instead of "#ctor"), or display the method name -->
        <h4 class="method_name">
        <xsl:choose>
          <xsl:when test="contains(@name, '#ctor')">
            Constructor: 
            <xsl:value-of select="$MemberName"/>
            <xsl:value-of select="substring-after(@name, '#ctor')"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="substring-after(@name, concat('M:',$FullMemberName,'.'))"/>
          </xsl:otherwise>
        </xsl:choose>
        </h4>
        
        <xsl:apply-templates select="summary"/>
        
        <!-- Display parameters if there are any -->
        <xsl:if test="count(param)!=0">
          <h5>Parameters</h5>
          <xsl:apply-templates select="param"/>      
        </xsl:if>
 
        <!-- Display return value if there are any -->
        <xsl:if test="count(returns)!=0">
          <!-- <h5>Return Value</h5> -->
          <xsl:apply-templates select="returns"/>      
        </xsl:if>
 
        <!-- Display exceptions if there are any -->
        <xsl:if test="count(exception)!=0">
          <h5>Exceptions</h5>
          <xsl:apply-templates select="exception"/>      
        </xsl:if>
 
        <!-- Display examples if there are any -->
        <xsl:if test="count(example)!=0">
          <h5>Example</h5>
          <xsl:apply-templates select="example"/>      
        </xsl:if>
 
      </xsl:for-each>
   
  </div>
  </xsl:if>
</xsl:template>
 
<!-- OTHER TEMPLATES -->
<!-- Templates for other tags -->
<xsl:template match="c">
  <code><xsl:apply-templates /></code>
</xsl:template>
 
<xsl:template match="code">
  <pre><xsl:apply-templates /></pre>
</xsl:template>
 
<xsl:template match="example">
  <p class="example"><strong>Example: </strong><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="exception">
  <p class="exception"><strong><xsl:value-of select="substring-after(@cref,'T:')"/>: </strong><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="include">
  <a href="{@file}">External file</a>
</xsl:template>
 
<xsl:template match="para">
  <p class="para"><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="param">
  <p class="param"><strong><xsl:value-of select="@name"/>: </strong><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="paramref">
  <span class="paramref"><xsl:value-of select="@name" /></span>
</xsl:template>
 
<xsl:template match="permission">
  <p class="permission">Permission: <em><xsl:value-of select="@cref" /> </em><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="remarks">
  <p class="remarks"><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="returns">
  <p class="returns"><strong>Return Value: </strong><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="see">
  <span class="see">See: <xsl:value-of select="@cref" /></span>
</xsl:template>
 
<xsl:template match="seealso">
  <span class="seealso">See also: <xsl:value-of select="@cref" /></span>
</xsl:template>
 
<xsl:template match="summary">
  <p class="summary"><xsl:apply-templates /></p>
</xsl:template>
 
<xsl:template match="list">
  <xsl:choose>
    <xsl:when test="@type='bullet'">
      <ul>
      <xsl:for-each select="listheader">
        <li><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></li>
      </xsl:for-each>
      <xsl:for-each select="list">
        <li><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></li>
      </xsl:for-each>
      </ul>
    </xsl:when>
    <xsl:when test="@type='number'">
      <ol>
      <xsl:for-each select="listheader">
        <li><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></li>
      </xsl:for-each>
      <xsl:for-each select="list">
        <li><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></li>
      </xsl:for-each>
      </ol>
    </xsl:when>
    <xsl:when test="@type='table'">
      <table>
      <xsl:for-each select="listheader">
        <th>
          <td><xsl:value-of select="term"/></td>
          <td><xsl:value-of select="definition"/></td>
        </th>
      </xsl:for-each>
      <xsl:for-each select="list">
        <tr>
          <td><strong><xsl:value-of select="term"/>: </strong></td>
          <td><xsl:value-of select="definition"/></td>
        </tr>
      </xsl:for-each>
      </table>
    </xsl:when>
  </xsl:choose>
</xsl:template>
 
</xsl:stylesheet>
 
 
 
 
 
====== CSS FILE =======
 
body {
	font-family: arial,helvetica,sans-serif;
}
 
p {
	padding: 0;
	margin: 3px;
}
 
h1, h2, h3, h4, h5 {
	padding: 0;
	margin: 0.5em 0.25em;
}
 
h1 {
	font-size: 2em;
}
 
h2 {
	font-size: 1.5em;
}
 
h3 {
	font-size: 1.25em;
}
 
h4, .param {
	font-size: 0.9em;
}
 
h5 {
	font-size: 0.75em;
}
 
h3, h5, .summary, .remarks, .param, .returns {
	margin-left: 2em;
}
 
.method_name {
	margin-top: 0.5em;
}
 
.members {
	margin-left: 2em;
	margin-bottom: 1em;
	border: 1px solid black;
	padding: 0.5em;
}
 
.paramref, see, seealso {
	font-style: italic;
}

Open in new window

LVL 1
BNLINDAsked:
Who is Participating?
 
MogalManicConnect With a Mentor Commented:
I couldn't get your XSL to work but I got the CodeProject documentation to sort by putting  on each for-each for each statement like this:
   
       
 

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ================================================================================ -->
<!-- Amend, distribute, spindle and mutilate as desired, but don't remove this header -->
<!-- A simple XML Documentation to basic HTML transformation stylesheet -->
<!-- (c)2005 by Emma Burrows -->
<!-- ================================================================================ -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<!-- DOCUMENT TEMPLATE -->
<!-- Format the whole document as a valid HTML document -->
<xsl:template match="/">
<HTML>
  <BODY>
    <xsl:apply-templates select="//assembly"/>
  </BODY>
</HTML>
</xsl:template>
 
<!-- ASSEMBLY TEMPLATE -->
<!-- For each Assembly, display its name and then its member types -->
<xsl:template match="assembly">
<H1><xsl:value-of select="name"/></H1>
  <xsl:apply-templates select="//member[contains(@name,'T:')]"/>
</xsl:template>
 
<!-- TYPE TEMPLATE -->
<!-- Loop through member types and display their properties and methods -->
<xsl:template match="//member[contains(@name,'T:')]">
 
  <!-- Two variables to make code easier to read -->
  <!-- A variable for the name of this type -->
  <xsl:variable name="MemberName"
                 select="substring-after(@name, '.')"/>
 
  <!-- Get the type's fully qualified name without the T: prefix -->
  <xsl:variable name="FullMemberName"
                 select="substring-after(@name, ':')"/>
 
  <!-- Display the type's name and information -->
  <H2><xsl:value-of select="$MemberName"/></H2>
  <xsl:apply-templates/>
 
  <!-- If this type has public fields, display them -->
  <xsl:if test="//member[contains(@name,concat('F:',$FullMemberName))]">
   <H3>Fields</H3>
 
      <xsl:for-each select="//member[contains(@name,concat('F:',$FullMemberName))]">
        <xsl:sort select="@name" data-type="text" order="ascending"/>
        <H4><xsl:value-of select="substring-after(@name, concat('F:',$FullMemberName,'.'))"/></H4>
        <xsl:apply-templates/>
      </xsl:for-each>
  </xsl:if>
 
  <!-- If this type has properties, display them -->
  <xsl:if test="//member[contains(@name,concat('P:',$FullMemberName))]">
  <H3>Properties</H3>
 
      <xsl:for-each select="//member[contains(@name,concat('P:',$FullMemberName))]">
        <xsl:sort select="@name" data-type="text" order="ascending"/>
        <H4><xsl:value-of select="substring-after(@name, concat('P:',$FullMemberName,'.'))"/></H4>
        <xsl:apply-templates/>
      </xsl:for-each>
  </xsl:if>
   
  <!-- If this type has methods, display them -->
  <xsl:if test="//member[contains(@name,concat('M:',$FullMemberName))]">
  <H3>Methods</H3>
   
    <xsl:for-each select="//member[contains(@name,concat('M:',$FullMemberName))]">
        <xsl:sort select="@name" data-type="text" order="ascending"/>
       
        <!-- If this is a constructor, display the type name 
            (instead of "#ctor"), or display the method name -->
        <H4>
        <xsl:choose>
          <xsl:when test="contains(@name, '#ctor')">
            Constructor: 
            <xsl:value-of select="$MemberName"/>
            <xsl:value-of select="substring-after(@name, '#ctor')"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="substring-after(@name, concat('M:',$FullMemberName,'.'))"/>
          </xsl:otherwise>
        </xsl:choose>
        </H4>
        
        <xsl:apply-templates select="summary"/>
        
        <!-- Display parameters if there are any -->
        <xsl:if test="count(param)!=0">
          <H5>Parameters</H5>
          <xsl:apply-templates select="param"/>      
        </xsl:if>
 
        <!-- Display return value if there are any -->
        <xsl:if test="count(returns)!=0">
          <H5>Return Value</H5>
          <xsl:apply-templates select="returns"/>      
        </xsl:if>
 
        <!-- Display exceptions if there are any -->
        <xsl:if test="count(exception)!=0">
          <H5>Exceptions</H5>
          <xsl:apply-templates select="exception"/>      
        </xsl:if>
 
        <!-- Display examples if there are any -->
        <xsl:if test="count(example)!=0">
          <H5>Example</H5>
          <xsl:apply-templates select="example"/>      
        </xsl:if>
 
      </xsl:for-each>
   
  </xsl:if>
</xsl:template>
 
<!-- OTHER TEMPLATES -->
<!-- Templates for other tags -->
<xsl:template match="c">
  <CODE><xsl:apply-templates /></CODE>
</xsl:template>
 
<xsl:template match="code">
  <PRE><xsl:apply-templates /></PRE>
</xsl:template>
 
<xsl:template match="example">
  <P><STRONG>Example: </STRONG><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="exception">
  <P><STRONG><xsl:value-of select="substring-after(@cref,'T:')"/>: </STRONG><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="include">
  <A HREF="{@file}">External file</A>
</xsl:template>
 
<xsl:template match="para">
  <P><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="param">
  <P><STRONG><xsl:value-of select="@name"/>: </STRONG><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="paramref">
  <EM><xsl:value-of select="@name" /></EM>
</xsl:template>
 
<xsl:template match="permission">
  <P><STRONG>Permission: </STRONG><EM><xsl:value-of select="@cref" /> </EM><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="remarks">
  <P><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="returns">
  <P><STRONG>Return Value: </STRONG><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="see">
  <EM>See: <xsl:value-of select="@cref" /></EM>
</xsl:template>
 
<xsl:template match="seealso">
  <EM>See also: <xsl:value-of select="@cref" /></EM>
</xsl:template>
 
<xsl:template match="summary">
  <P><xsl:apply-templates /></P>
</xsl:template>
 
<xsl:template match="list">
  <xsl:choose>
    <xsl:when test="@type='bullet'">
      <UL>
      <xsl:for-each select="listheader">
        <LI><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></LI>
      </xsl:for-each>
      <xsl:for-each select="list">
        <LI><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></LI>
      </xsl:for-each>
      </UL>
    </xsl:when>
    <xsl:when test="@type='number'">
      <OL>
      <xsl:for-each select="listheader">
        <LI><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></LI>
      </xsl:for-each>
      <xsl:for-each select="list">
        <LI><strong><xsl:value-of select="term"/>: </strong><xsl:value-of select="definition"/></LI>
      </xsl:for-each>
      </OL>
    </xsl:when>
    <xsl:when test="@type='table'">
      <TABLE>
      <xsl:for-each select="listheader">
        <TH>
          <TD><xsl:value-of select="term"/></TD>
          <TD><xsl:value-of select="definition"/></TD>
        </TH>
      </xsl:for-each>
      <xsl:for-each select="list">
        <TR>
          <TD><strong><xsl:value-of select="term"/>: </strong></TD>
          <TD><xsl:value-of select="definition"/></TD>
        </TR>
      </xsl:for-each>
      </TABLE>
    </xsl:when>
  </xsl:choose>
</xsl:template>
 
</xsl:stylesheet>

Open in new window

0
 
MogalManicCommented:
Your sort should be a valid XPATH expression.  So change the sort to:
     
If you want to sort by multiple keys, just add more  elements.  The order or the elements determine the sort order
0
 
BNLINDAuthor Commented:
On line 13 of the code above, I was playing around with the "member>@name" and didn't mean for that to be in there. I made the change suggested by MogalManiac, but the class names are still not sorted.
0
 
BNLINDAuthor Commented:
I ended up grabbing a book about XSLT and XPath and wrote my own, using the CodeProject version for reference. Once I knew a little more about XPath and XSLT's in general, I was able to see where I messed up. Even though your sample above is correct and works, I found out that I still need to make several modifications so that I can also use CSS to make it pretty.

There really is no "quick-fix" with this stuff....you really have to know it, but it was actually pretty easy once I stopped trying to fix the problem and started learning the language. It only took about an hour to figure it all out once I started reading the book and now I can write my own XSLT files :)

Thanks for your help with this.
0
 
MogalManicCommented:
A good book on XSLT is XSLT: Programmer's Reference published by Wrox press.

I use the 2nd addition which looks like it is out of print:
  http://www.wrox.com/WileyCDA/WroxTitle/productCd-0764543814.html

But there is a XSLT 2.0 Programmer's Reference, 3rd Edition that looks like the next version:
  http://www.wrox.com/WileyCDA/WroxTitle/XSLT-2-0-Programmer-s-Reference-3rd-Edition.productCd-0764576496.html

The author Michael Kay is the writer of the SAXON XSLT processor
  http://en.wikipedia.org/wiki/Saxon_XSLT
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.