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)" xmlns:ScriptNS0="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 xmlns:ns0="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 xmlns:ns0="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.