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)" 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.
Saturday, March 15, 2008 11:30:27 AM (GMT Standard Time, UTC+00:00) #    Comments [0]  |  Trackback

 

Comments are closed.
All content © 2020, Daniel Probert
On this page
This site
Calendar
<July 2020>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Archives
Sitemap
Blogroll OPML
Disclaimer

Powered by: newtelligence dasBlog 2.3.12105.0

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Send mail to the author(s) E-mail

Theme design by Jelle Druyts


Pick a theme: