Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

Sort C# XML Documentation Output by Class Name

Posted on 2009-04-01
5
Medium Priority
?
1,008 Views
Last Modified: 2013-11-18
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

0
Comment
Question by:BNLIND
  • 3
  • 2
5 Comments
 
LVL 21

Expert Comment

by:MogalManic
ID: 24048355
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
 
LVL 1

Author Comment

by:BNLIND
ID: 24048992
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
 
LVL 21

Accepted Solution

by:
MogalManic earned 2000 total points
ID: 24049446
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
 
LVL 1

Author Comment

by:BNLIND
ID: 24050943
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
 
LVL 21

Expert Comment

by:MogalManic
ID: 24053189
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

Featured Post

Free learning courses: Active Directory Deep Dive

Get a firm grasp on your IT environment when you learn Active Directory best practices with Veeam! Watch all, or choose any amount, of this three-part webinar series to improve your skills. From the basics to virtualization and backup, we got you covered.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

JavaScript has plenty of pieces of code people often just copy/paste from somewhere but never quite fully understand. Self-Executing functions are just one good example that I'll try to demystify here.
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
The viewer will learn how to dynamically set the form action using jQuery.
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Suggested Courses

885 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question