Debugging XSLT Stylesheet with Custom Extension Objects from Within Visual Studio

When developping complex maps with the BizTalk Mapper, you sometimes find yourself in a situation where you need to debug the underlying logic of the associated XML stylesheet.

In situations like these, it is customary to have Visual Studio generate an XSLT file from the BizTalk map, and use Visual Studio’s builtin support for debugging XSLT. This allows you to put breakpoints, inspect the content of variables and precisely monitor the transformation step by step.

Custom Extension Objects

There are two cases, however, that prevent you from debugging the underlying XSLT from within Visual Studio :

The first case involves the use of the various database functoids, for instance the cross-reference GetCommonValue, GetCommonID, GetApplicationValue and GetApplicationID functoids. These, are implemented in terms of external functions, in a separate assembly, that is itself referenced from the generated map.

The second case involves the use of the Scripting functoid to call an external function. This is a variation of the first case in which you specify the function you want to call.

In order to execute such maps, the BizTalk Mapper uses the XslTransform class by supplying a list of all the classes whose methods are required to carry out the XSLT transformation, along with their associated namespace prefixes.

The set of such custom classes used to provide external logic to XLST transformations is referred to as Custom XSLT Extension Objects. At design-time, these Custom Extension Objects are represented as an additional file that contains the associations between the declared XML namespace prefixes and the fully qualified .Net type names of the classes which contain the methods called from the map.

When you invoke the “Validate Map” context-menu option on a BizTalk Map in Visual Studio, you will notice that BizTalk generates this file, with a _extxml.xml suffix along with the associated XSLT.

Most of the time, this file is empty, for basic maps. This is not the case, however, if the map makes use of external functions in separate assemblies, as alluded to in the two cases described above.

Debugging XSLT with Custom Extension Objects

Unfortunately, such maps seemingly cannot be debugged inside Visual Studio without modifications. If you use Visual Studio’s builtin XSLT debugger and attempt to step into the underlying XSLT file, you will encounter an error such as this one:

---------------------
Cannot find the script or external object that implements prefix
'http://schemas.microsoft.com/BizTalk/2003/ScriptNS0'. "

Even though Visual Studio generates both an XSLT file and a _EXTXML.XML file, it seems there is no way to instruct Visual Studio about the custom extension objects file, which makes the debugging experience cumbersome and very frustrating.

Fortunately, there is a way!

Although there are no options in the Visual Studio GUI, all the support is there in the .Net Framework. All there is do is write a little C# program to do the trick.

A Simple Function to Debug Custom XLST Maps

The following function is very simple. In real life, this function would be compiled into a C# program with additional logic to accept and parse command-line arguments, in order to be able to supply paths to the stylesheet, the extension objects file and the input and output documents. This is no rocket science.

Here is the code:

using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml; 
using System.Xml.Xsl;

class DebugHelper
{
  static void Main(string[] args)
  {
    string input = @"...\input.xml";
    string output = Path.ChangeExtension(input, ".out");

    string stylesheet = @"...\stylesheet.xslt";
    string extension_object = @"...\extension_extxml.xml";

    string result = TransformXslt(input, stylesheet, extension_object);
  }

  private static string TransformXslt(string document, string stylesheet, string extension)
  {
    return TransformXslt(document, stylesheet, ParseExtension(extension));
  }

  private static string TransformXslt(string document, string stylesheet, object[] extension)
  {
    XslCompiledTransform transform = new XslCompiledTransform(true);
    transform.Load(stylesheet, new XsltSettings(true, true), null);

    XsltArgumentList arguments = new XsltArgumentList();

    for (int index = 0; index < extension.Length; index += 2)
    {
      arguments.AddExtensionObject(
        extension[index] as string,
        extension[index + 1]
        );
    }

    StringBuilder output = new StringBuilder();

    using (XmlWriter writer = XmlWriter.Create(output, transform.OutputSettings))
      transform.Transform(document, arguments, writer);

    return output.ToString();
  }

  private static object[] ParseExtension(string extension)
  {
    if (String.IsNullOrEmpty(extension))
      return new object[]{};

    XmlDocument document = new XmlDocument();
    document.Load(extension);

    ArrayList extensions = new ArrayList();

    foreach (XmlNode node in document.SelectNodes("/ExtensionObjects/ExtensionObject"))
    {
      string extension_namespace = node.Attributes["Namespace"].Value;
      string extension_assembly = node.Attributes["AssemblyName"].Value;
      string extension_class = node.Attributes["ClassName"].Value;
      string assembly_qualified_name = String.Format("{0}, {1}"
        , extension_class
        , extension_assembly
        );

      object extension_object = Activator.CreateInstance(Type.GetType(assembly_qualified_name));
      
      extensions.Add(extension_namespace);
      extensions.Add(extension_object);
    }

    return extensions.ToArray();
  }
}

A couple of points of interest need clarification.

First, the most crucial step, is to use the XslCompiledTransform constructor with a first parameter that indicates whether to execute the map in a debugger. This is actually what does the trick in this program.

When you run the program, it will launch the debugger and break at the beginning of the map. You can add this project to your Visual Studio solution, and you will be able to step into the map directly from the same Visual Studio instance.

Neat.

Secondly, what distinguishes this program from Visual Studio’s builtin XSLT debugger is the ability to actually make use of Custom XSLT Extension Objects.

The ParseExtensions function has been designed to consume an XML file, whose path is specified in the first argument, that has the same format as the ones generated by Visual Studio when using the “Validate Map” option.

This entry was posted in BizTalk, Tips. Bookmark the permalink.

13 Responses to Debugging XSLT Stylesheet with Custom Extension Objects from Within Visual Studio

  1. twh says:

    This looks like an extremely useful technique. However, the first map I tried it on gave me the error “Extension functions cannot return null values”.

    It is correct – the function I am calling does indeed return a null for some input values. This is intentional, and the normal mapping engine handles this behavior quite well. Is this a peculiarity with XslCompiledTransform, and is there any way around it?

  2. Toraj says:

    This is awsome – Please display your contact info.

  3. Ricky says:

    Hi , how would you resolve exception, “However, the first map I tried it on gave me the error “Extension functions cannot return null values” … how can we bypass that if a null is an intentened value??? Thanks…

  4. Pingback: The biggest change in BizTalk 2013 and how to undo it | danrosanova

  5. chinna says:

    I’ve created a console application with the code provided in Visual Studio 2010. However, when I run the application it simply ends the execution without showing up any output. It neither opens up the xslt in debugging mode nor display any message. Greatly appreciate your help if you can let me know if I’m missing something here.

  6. chinna says:

    Got it. Previously I wasn’t stepping in at Transform method.

  7. jglisson says:

    Reblogged this on John Glisson – Geek of the Cloth and commented:
    This blog post was very useful today!

  8. Rob Bowman says:

    Great post, very helpful thanks Maxime

  9. Dennis says:

    This is a great article!

    Like chinna, at first glance I did not get/saw any result because I did not set the a breakpoint at the line “transform.Transform(document, arguments, writer);”. After that, it worked great.

    Minor remark: In the code, the ‘output’ variable in the Main method is not used.

    I also have the following question:
    When running the XSLT map debugger in Visual Studio, the generated output, as it is building up while debugging, is shown in it’s own window. Is it possible to have the same here?

    With kind regards,

    Dennis
    The Netherlands

  10. Rylsngrd says:

    Fantastic help. Really needed this in a pinch. Super easy to get up and testing my maps. thanks a ton!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s