I have a love:hate relationship with XSLT. It's frighteningly powerful, but oftimes seems just a bit beyond my grasp. Also, I don't use it often enough to get good at it.

If you set a progress bar watching me write some XSLT, it would look something like:

0-0-1-1-5-20-90

(In other words, it takes me forever to get started, then suddenly I'm almost done)

I was doing some of this today for one of our internal tools (the latest version of Duncan "The Tool Kitten" Mackenzie's Page Planner), trying to transform one document into another. I thought I had reached a roadblock when I realized I needed (don't ask) a unique GUID for one of the nodes. Originally, I thought I'd have to force the TK to add the GUID to his source document. Then, I remembered the msxsl:script extension MSFT had added to the old MSXML parser. Was it still there? Yes. Even better, it now supports .NET languages! So, I could add my GUIDs myself! What I ended up with was (unimportant stuff removed to protect the incompetant -- myself)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:utility="urn:myScripts">
<msxsl:script language="vb" implements-prefix
="utility">
function NewGuid
  return System.Guid.NewGuid.ToString()
end function
</msxsl:script>

<xsl:template match="/">
  <toc xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance">
    <xsl:apply-templates
/>
  </toc
>
</xsl:template>

<xsl:template match="Pages">
 
<pageGroup
>
   
<xsl:attribute name
="guid">
     
<xsl:value-of select="utility:newGuid()"
/>
    </xsl:attribute
>
 
</pageGroup
>
</xsl:template>

</xsl:stylesheet>

The important part of the script is the declaration of the script in an msxsl:script block (or any namespace prefix that maps to urn:schemas-microsoft.com:xslt). Here I have it defined using "VB", but you can also use "CSharp", or any other .NET language. The other required attribute is the name you want to give that script block. We'll use that later.

Later, you can call the script by using the value-of element from XSLT. You set the select as shown to <name you gave your script block>:<Procedure name>. Whatever the function evaluates to will be put into the resulting file.

What if you want to pass parameters? Easily done, you simply pass in the desired element (or attribute as done here):

<msxsl:script language="vb" implements-prefix="utility">
function GetMatchingXmlFile(byval aspxFile as string)
  return System.IO.Path.ChangeExtension(aspxFile, ".xml")
end function
</msxsl:script>

<xsl:template match="page">
<Page
>
 
<PageType>navpage</PageType
>
 
<XMLFileName
>
   
<xsl:value-of select="utility:GetMatchingXmlFile(@url)"
/>
 
</XMLFileName
>
</Page
>
</xsl:template>

[Listening to my wife practice her violin, and tell me I should be exercising]