Solved

Sort C# XML Documentation Output by Class Name

Posted on 2009-04-01
5
970 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 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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Styling your websites can become very complex. Here I'll show how SASS can help you better organize, maintain and reuse your CSS code.
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
The viewer will learn how to count occurrences of each item in an array.

747 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now