Tech Blog

System.Data.SqlXmlHelper – calling extended XPath functions from BizTalk Maps

In this post:
   Methods in the helper class

   Notes on implementation

   Installation Instructions

   Problems with the Script Functoid

   Examples of Usage

   Download the code

   Testing the sample map

I’m not sure if this is actually of any use to anyone but if you do want to use Microsoft’s
Extended XPath Functions
from a BizTalk Map (see my previous
post
) then I’ve created a wrapper assembly to do this for you.
Method names are the same as the original functions, but using “_” instead
of “-“.

Methods in the helper class


Method

Description

format_date

Formats an XSD dateTime value into a date string using the given format string
and culture


Usage:

format_date(dateTime, [lang], [format])

 
 

dateTime: string containing XSD dateTime value

lang: Optional culture string – if blank or not supplied
then current culture is used


format: Optional date format string (same format as DateTime.ToString(string))
– if blank or not supplied then default date format is used


Example:

format_date(‘2000-02-16T15:56:00+02:00’, ”, ‘MMM dd, yyyy’)

format_time

Formats an XSD dateTime value into a time string using the given format string
and culture


Usage:

format_time(dateTime, [lang], [format])

 
 

dateTime: string containing XSD dateTime value

lang: Optional culture string – if blank or not supplied
then current culture is used


format: Optional time format string (same format as DateTime.ToString(string))
– if blank or not supplied then default time format is used


Example:

format_date(‘2000-02-16T15:56:00+02:00’, ”, ‘hh mm ss’)

local_name

Strips a namespace or namespace prefix from a node name

Usage:

local_name(nodeName) or

local_name(node)

 
 

nodeName: qualified node name (e.g. as supplied by name() expression)

node: XPathSelectionIterator object,
what you get when you select a node in a map via XSLT


Example:

local_name(‘ns0:root’)

namespace_uri

Looks up the namespace for a given namespace-prefix

Usage:

namespace_uri(nodeName, currentNode)

namespace_uri(nodeName, xmlFragment)

namespace_uri(nodeName, list)

 
 

nodeName: prefixed node name

currentNode: XPathSelectionIterator object
referencing the current node


xmlFragment: fragment of xml, supplied as a string

list: list containing a single XPathItem

Example:

namespace_uri (‘ns0:root’, current())

number

Converts a given string into a double number. Supports Exponential and INF statements

Usage:

number(value)

 
 

value: string to be converted to a double. Can also be
exponential (i.e. ‘1e023’, or ‘-INF’ or ‘INF’)


Example:

number(‘INF’)

string_compare

Compares two strings, with optional case/culture sensitivity

Usage:

string_compare(s1, s2, [lang], [options])

 
 

s1: first string to compare

s2: second string to compare

lang: Culture to use for comparison – if blank or not supplied
then current culture is used


options:

‘u’: case-sensitive, uppercase-first comparison

‘i’: case-insensitive comparison

” (or not supplied): case-sensitive, lowercase-first comparison

Example:

string_compare(‘the brown cow’, ‘The Brown Cow’, ”, ‘u’)

Note: the return value is same as for String.Compare() in
C#.


utc

Converts an XSD dateTime value into coordinated universal time (i.e. GMT)

Usage:

utc(dateTime)

 
 

dateTime: string containing XSD dateTime value

Example:

utc(‘2000-02-16T15:56:00+02:00’)

Note: the following methods are not in this class

  • schema-info-available
  • type-is
  • type-local-name
  • type-namespace-uri

The reason for this is that these functions are not implemented in System.Data.SqlXml.dll
they’re in MSXML.
Admittedly they’d be fairly easy to implement, but the purpose of this wrapper is
to expose the functions in the XsltFunctions class
in System.Data.SqlXml.dll.

The source is fully documented, so hopefully it’s clear(ish) how to use the helper
class.

Notes on implementation

For all but two of the functions, I just call the corresponding method in XsltFunctions (providing
some method overloads).
However namespace_uri() and number() are
different.
The signatures for these methods in XsltFunctions are:

public static string MSNamespaceUri(string name, XPathNavigator currentNode)

public static double MSNumber(IList<XPathItem>
value)

In order to use these from a BizTalk Map, I added some method overloads which can
call the above methods with the correct parameter types.

  • namespace_uri

    the most likely way to use this will be by passing it a node from a map – this will
    need to be done from inside a Script functoid using custom XSLT (see the examples
    later).
    Therefore there is an overload which takes in an XPathNodeIterator (which
    is the base class of the XPathSelectionIterator object
    you get when you select a node in a map and pass it to an external assembly’s method)
  • number
    the string that is passed in is wrapped in an XML node, and then loaded into an
    XPathDocument, and from this an XPathNavigator is created which can be passed (in
    a list) to the internal XsltFunctions.MSNumber method
    (where it’s unwrapped back into a string again!!)

Installation Instructions

Just add the DLL to the GAC (it’s already signed) or build the source code project,
and then GAC the compiled assembly.
Note: this version is compiled against v2.0.50727.1433 of System.Data.SqlXml.dll
if you have a different version of System.Data.SqlXml.dll then
you’ll need to add a reference to your version, and re-compile the project.

Problems with the Script Functoid

If you’ve ever tried to pass a node to an external assembly using the script functoid,
you’ve probably already noticed something: the script functoid explicitly converts
all input parameters to strings by wrapping a string() expression
around the parameters.
For example, if you do this:

(i.e. use a record as the input parameter to a script functoid) then you’ll soon discover
that the Mapper always assumes that you want the value of the node, not the node
itself
. And it therefore helpfully wraps the node in a string() expression
(adding a text() expression
if necessary):

<xsl:variable name=”var:v5″
select=”ScriptNS0:namespace_uri(string(./text()))” />

So the only way to pass a node/element as a parameter is to use custom XSLT in a script
functoid – an example of this is provided below.

Examples of Usage

Using functions where all parameters are strings

For example, using format_date().
Add the System.Data.SqlXmlHelper.dll as
a reference to your BizTalk project.
In a BizTalk Map, add a script functoid, choose External Assembly as the Script
Type
, and then choose the System.Data.SqlXmlHelper assembly,
and the System.Data.SqlXmlHelper.Helper class.
Next choose the method you wish to use, such as the format_date method:

Next, add some inputs to the script functoid – in this example, I’ve used constants
for the inputs, but they could easily be links from other nodes:

Now, just link the output of your script functoid to a node on the destination schema.
And that’s it!

Using functions where you need an XPathNodeIterator parameter

namespace_uri is an
example of an expression that needs context i.e. a current node to process.
In this case, the only way to pass a current node to the method is via custom XSLT
in a script functoid.

You can do this by using an XSLT expression such as this one in a Custom XSLT script
functoid


<xsl:variable name=currentNodeName select=name(/*[local-name()=’Employees’
and namespace-uri()=’http://TestMaps.Employees’])
/>


<xsl:variable name=currentNode select=/*[local-name()=’Employees’
and namespace-uri()=’http://TestMaps.Employees’]
/>


<xsl:attribute name=expandedPrefix>

       <xsl:value-of select=ScriptNS0:namespace_uri($currentNodeName,
$currentNode)
/>


</xsl:attribute>

Note: the above example assumes that the ScriptNS0 prefix
is already defined in the map and linked to an Extension Object for the System.Data.SqlXmlHelper.dll assembly.
This will be true if you already have a script functoid using System.Data.SqlXmlHelper.dll as
an external assembly in the same map.
If you don’t, then you’ll need to generate a separate ExtensionObjects xml file and
put this in it:


<ExtensionObjects>

<ExtensionObject Namespace=http://schemas.microsoft.com/BizTalk/2003/ScriptNS0 AssemblyName=System.Data.SqlXmlHelper,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=126dcf2731b780d4
ClassName=System.Data.SqlXmlHelper.Helper />


</ExtensionObjects>

You will also need to manually add a definition for the script namespace prefix to
the custom XSLT e.g.


<xsl:variable name=currentNodeName select=name(/*[local-name()=’Employees’
and namespace-uri()=’http://TestMaps.Employees’])
/>


<xsl:variable name=currentNode select=/*[local-name()=’Employees’
and namespace-uri()=’http://TestMaps.Employees’]
/>


<xsl:attribute name=expandedPrefix>

       <xsl:value-of select=ScriptNS0:namespace_uri($currentNodeName,
$currentNode)
>=http://schemas.microsoft.com/BizTalk/2003/ScriptNS0 />


</xsl:attribute>

(although you will probably want to use a separate prefix than ScriptNS0 as
you could clash with any prefix added later)

Download the code

You can download the assembly and full source code here.
The source code solution includes a BizTalk test project with a sample map in it.

System.Data.SqlXmlHelper.dll
(20 KB)


System.Data.SqlXmlHelper
Source Code.zip (16.71 KB)

(Note: in the sample BizTalk project, you right-click the sample map and choose
Test Map to see the results – if you get an error make sure you have GACced the System.Data.SqlXmlHelper.dll assembly).

Testing the sample map

Included in the BizTalk solution is a sample map which demonstrates execution of all
the functions:

In order to use this map, you’ll need to change the TestMap Input Instance property
to point at the SqlXmlHelperTest_Instance.xml file (located in the SampleXML folder
of the BizTalk project).
The map takes this input:


<ns0:Record >=http://System.Data.SqlXmlHelper.BizTalkTest.Schemas.SqlXmlHelperTest>

<ns0:Input>

    <ns0:date>1999-05-31T13:20:00.000-05:00</ns0:date>

    <ns0:text1>I
am a Node</ns0:text1>


    <ns0:text2>i
am a node</ns0:text2>


    <ns0:numberAsString>1.2e-3</ns0:numberAsString>

</ns0:Input>

</ns0:Record>

And transforms it to this output:


<ns0:Record >=http://System.Data.SqlXmlHelper.BizTalkTest.Schemas.SqlXmlHelperTest>

       <ns0:Output>

              <ns0:format-date>May
31, 1999</ns0:format-date>


              <ns0:format-time>06/20/00</ns0:format-time>

              <ns0:utc>1999-05-31T18:20:00.000</ns0:utc>

              <ns0:string-compare>1</ns0:string-compare>

              <local-name>Input</local-name>

              <namespace-uri>http://System.Data.SqlXmlHelper.BizTalk

              Test.Schemas.SqlXmlHelperTest</namespace-uri>

              <ns0:number>0.0012</ns0:number>

       </ns0:Output>

</ns0:Record>

(Note: the pattern used for format_date is
“MMM dd, yyyy”, and the pattern for format_time is
“hh/mm/ss”)

Let me know if you find it useful.

Back to Tech Blog