Posted by: techsavygal | July 3, 2006

Mapping in BizTalk Server 2004/2006 without a Map!

Hi All,

While looking for some information on internet, i came across this interesting data that we can map (read transform data) between different formats without using a Map in BizTalk Server 2004/2006.

If you have XSLT code you have been using to convert instance messages, you can use that code directly instead of creating a map. Using your XSLT code involves creating an empty map and setting its Custom XSLT Path grid property. If your XSLT code uses external .NET assemblies, you will also need to create a custom extension XML file.

1.First you need to extract the XSL document (ideally taken from Mapper)
2.If the map uses functoids (out of the box or custom functoids), you will need to extract the Xsl Transformation arguments,
3.Create a .NET System.Xml.Xsl.XslTransfom() object,
4.Create a .NET System.Xml.Xsl.XsltArgumentList() if there were any functoids in the map and instantiate appropriate objects,
5.Call Transform() on the XSL and optionally, the XsltArgumentList.
In the list of steps above 3, parts of 4 and 5 have nothing to do with BizTalk: these are just plain .NET programming. Creating the XsltArgumentList requires us to understand how the mapper saves functoids.

Extracting the XSL and the extension objects (if any) can be achieved by at least three different methods:

1. If you have the map file (.btm) and can open it in Visual Studio 2003, you can right click on the map file in the solution explorer and select “Validate Map”. The output window will give you the path(s) to the XSL and the extension Object XML. The links can be shift-clicked to retrieve the files,
2.If you only have the compiled assembly, you can use the excellent Lutz Roeder’s .NET Reflector to extract the required information as strings,
If you only have the compiled assembly, you can write some code that loads the assembly, creates an instance of the map object and calls the appropriate members. See the format of maps assemblies.
3.The only speed bump is the format of the Extension Object XML document. I have extracted the extension associated with the map Scriptor_CallExternalAssembly from the “ExtendingMapper” SDK sample and formatted it:

<ExtensionObjects>
  <ExtensionObject Namespace=”http://schemas.microsoft.com/BizTalk/2003/ScriptNS0
         AssemblyName=”Microsoft.Samples.BizTalk.ExtendingMapper.MapperClassLibrary,
         Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2aaad746c3d94f5″
         ClassName=”Microsoft.Samples.BizTalk.ExtendingMapper.MapperHelper” />
</ExtensionObjects>

Creating the extension objects is now very simple. For each “ExtensionObject” node, we need to load the assembly, create an instance of the given class and add the object along with its namespace to the XsltArgumentList. Of course, the map will only run if all needed assemblies are available. This is true for custom assemblies as well as out of the box functoids.

using System;
using System.Reflection;
using System.Xml;
using System.Xml.Xsl;
using System.IO;
using System.Text;
namespace MapReuser
{
 /// <summary>
 /// Transforms XML instances using a BizTalk map.
 /// </summary>
 public class BizTalkMap
 {
  /// <summary>
  /// Caches the XSLT stream.
  /// </summary>
  private Stream xsltStream;

  /// <summary>
  /// Caches the XSLT Arguments stream.
  /// </summary>
  private Stream xsltArguments;
  

  /// <summary>
  /// Cache the XslTransform.
  /// </summary>
  private XslTransform     xslTransform;

  /// <summary>
  /// Caches the XSltArgumentList.
  /// </summary>
  private XsltArgumentList xslArgumentList;
  /// <summary>
  /// Constructor.
  /// </summary>
  /// <param name=”XsltStream”>Stream of XSLT as XML.</param>
  /// <param name=”XsltArguments”>Stream of Extension Objects as XML.</param>
  public BizTalkMap(Stream XsltStream, Stream XsltArguments)
  {
   xsltStream    = XsltStream;
   xsltArguments = XsltArguments;
  }

  /// <summary>
  /// Transforms the given instance and returns the result as a stream.
  /// </summary>
  /// <param name=”inXml”>Stream of the instance to transform (XML)</param>
  /// <returns>Stream of the transformed XML.</returns>
  public Stream TransformInstance(Stream inXml)
  {
   XslTransform transform   = Transform;
   XmlDocument  xmlInputDoc = new XmlDocument();

   // Make sure we do not destroy the formatting
   xmlInputDoc.Load(inXml);

   // Output stream
   MemoryStream  outStream = new MemoryStream();
   XmlTextWriter xmlWriter = new XmlTextWriter(outStream, System.Text.Encoding.UTF8);

   // Formatting options
   xmlWriter.Formatting  = Formatting.Indented;
   xmlWriter.Indentation = 2;
   
   // Perform transformation – We do not specify a resolver
   transform.Transform(xmlInputDoc, TransformArgs, xmlWriter, null);

   // Prepare the output stream
   outStream.Seek(0, SeekOrigin.Begin);

   return outStream;
  }

  /// <summary>
  /// Gets an instance of XslTransform for the given XSL/Extension objects.
  /// </summary>
  private XslTransform Transform
  {
   get
   {
    if (xslTransform == null)
    {
     // Create a new transform
     XmlTextReader xsltReader = new XmlTextReader(xsltStream);
     XslTransform transformTemp = new XslTransform();
     transformTemp.Load(xsltReader, (XmlResolver) null, GetType().Assembly.Evidence);
     
     // Cache the transform
     xslTransform = transformTemp;
    }
    return xslTransform;
   }
  }

  /// <summary>
  /// Gets a XsltArgumentList from a BizTalk Extension Object XML.
  /// </summary>
  private XsltArgumentList TransformArgs
  {
   get
   {
    if (xslArgumentList == null)
    {
     XmlDocument      xmlExtension = new XmlDocument();
     XsltArgumentList xslArgList   = new XsltArgumentList();

     if (xsltArguments != null)
     {
      // Load the argument list and create all the needed instances
      xmlExtension.Load(xsltArguments);
      XmlNodeList xmlExtensionNodes = xmlExtension.SelectNodes(“//ExtensionObjects/ExtensionObject”);
      foreach (XmlNode extObjNode in xmlExtensionNodes)
      {
       XmlAttributeCollection extAttributes = extObjNode.Attributes;

       XmlNode  namespaceNode = extAttributes.GetNamedItem(“Namespace”);
       XmlNode  assemblyNode  = extAttributes.GetNamedItem(“AssemblyName”);
       XmlNode  classNode     = extAttributes.GetNamedItem(“ClassName”);
       Assembly extAssembly   = Assembly.Load(assemblyNode.Value);
       object   extObj        = extAssembly.CreateInstance(classNode.Value);
       xslArgList.AddExtensionObject(namespaceNode.Value, extObj);
      }
     }
     // Cache the list
     xslArgumentList = xslArgList;
    }
    return xslArgumentList;
   }
  }
 }
}

This class can be used as follows:
FileStream fsXslt       = null;
FileStream fsInput      = null;
FileStream fsExtensions = null;
FileStream outStream    = null;

try
{
 // Transform
 fsXslt         = new FileStream(xsltPath, FileMode.Open, FileAccess.Read);
 fsInput        = new FileStream(instancePath, FileMode.Open, FileAccess.Read);
 fsExtensions   = (extensionPath != null) && (extensionPath.Length > 0) ? new FileStream(extensionPath, FileMode.Open, FileAccess.Read) : null;
 BizTalkMap map = new BizTalkMap(fsXslt, fsExtensions);

 Stream sOut = map.TransformInstance(fsInput);

 // Save stream to a file
 string destPath = Path.Combine(Path.GetDirectoryName(instancePath), Path.GetFileName(instancePath) + “.trans.xml”);
 outStream = new FileStream(destPath, FileMode.Create, FileAccess.Write);
 outStream.Write(((MemoryStream) sOut).ToArray(), 0, (int) sOut.Length);
}
finally
{
 if (fsXslt       != null) fsXslt.Close();
 if (fsInput      != null) fsInput.Close();
 if (fsExtensions != null) fsExtensions.Close();
 if (outStream    != null) outStream.Close();
}

Advertisements

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

Categories

%d bloggers like this: