Solved

Sort C# XML Documentation Output by Class Name

Posted on 2009-04-01
5
983 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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 500 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

How our DevOps Teams Maximize Uptime

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us. Read the use case whitepaper.

Question has a verified solution.

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

A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
SASS allows you to treat your CSS code in a more OOP way. Let's have a look on how you can structure your code in order for it to be easily maintained and reused.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
The viewer will receive an overview of the basics of CSS showing inline styles. In the head tags set up your style tags: (CODE) Reference the nav tag and set your properties.: (CODE) Set the reference for the UL element and styles for it to ensu…

739 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