| Programmer to ProgrammerTM | |||||
|
|||||
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
| |||||||||||||||||||||
| The ASPToday
Article August 21, 2000 |
Previous
article - August 18, 2000 |
Next
article - August 22, 2000 | |||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||
| ABSTRACT |
| ||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||
| Article Discussion | Rate this article | Related Links | Index Entries | ||||||||
| ARTICLE | |||||||||||
In this, the final part of the SOAP/XMLHTTP series, we’ll look at using XSLT (eXtensible Stylesheet Language for Transformation) to process SOAP requests. Whilst this might sound like an unorthodox approach to handling SOAP requests, it’s actually the mechanism at the heart of the BizTalk mapper – and it does prove that XML is a very flexible and concise mechanism for Application–to–Application (A2A) and Business–to–Business (B2B) communication.
In part one we examined the SOAP specification and presented a small employee application to put our knowledge into practice. Part two provided us with enough information to make our employee application a little more robust by providing fault handling. We’re going to change direction in this article – the employee application will be replaced with a currency converter. This is an ideal application for a true web service or even as a web part for a digital dashboard.
Part of the motivation for this example is to emphasize last minute database access. Currency rates change minute–to–minute, thus when the user requests a conversion, it should be the most recent rate – not the rate we sent to the browser (using XML of course!) a few minutes earlier.
My previous articles worked with Internet Explorer 5 and the original release of MSXML.DLL. The example I’m presenting here requires the May 2000 (or later) release of MSXML3.DLL. Microsoft are endeavoring to release a new version of their XML parser (and XSLT processor) every 6 – 8 weeks. As of July 31, the July 2000 release (with installation instructions) is available for download from here.
This article will assume a basic knowledge of XSLT – although I do explain my stylesheets in some detail. If you’re familiar with what is now becoming known as XSL 98 (as implemented in IE5 without MSXML3.DLL), then you should have no problems following what’s going on. The full W3C specification for XSLT can be found: here.
I’ve created a small Access based example – the database contains one table, tblCurrencies , and has five records:

The database forms part of the download associated with this article – the one thing you’ll need to do is create a DSN called ASPToday and point it at this database. The client–side HTML page is fairly simple: there is an input control that accepts an amount in Sterling, a dynamic drop–down that is populated from the database, and an area to display the converted amount:

Assuming a valid number is entered into the Sterling amount field, and a Currency is selected, clicking on Convert sends a SOAP Request to the server. The SOAP Request is similar to this one:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope"> <SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope"> <m:getCurrency xmlns:m="http://www.craigmurphy.com/hrweb"> <currencyID>2</currencyID> <SterlingAmount>900</SterlingAmount> </m:getCurrency> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
Here’s the code behind the Convert onClick() :
function convert(sCurrency, sAmount) {
currency = cmSOAP( 'getCurrency',
cmSOAPrtXMLDOM,
'currencyID', sCurrency,
'SterlingAmount', sAmount);
txt_converted_amount.disabled = false;
var SOAPEnvelope = currency.selectSingleNode('SOAP-ENV:Envelope');
var SOAPBody = SOAPEnvelope.selectSingleNode('SOAP-ENV:Body');
var getCurrencyResponse = SOAPBody.selectSingleNode('*/result');
txt_converted_amount.value = getCurrencyResponse.text;
}
As you can see, it’s doing no more than the examples we’ve seen so far. We’re using cmSOAP() to relieve ourselves of the burden of creating a SOAP Request, we would like an XMLDOM to be returned, and we specify the currencyID and SterlingAmount as elements inside the SOAP Request’s Body element. Once a SOAP response is received, the result element is extracted and the Converted Amount field is populated.
I’ve may have been a little over–zealous with my use of selectSingleNode() – however, it does make for more readable code.
The Microsoft implementation of XSLT brings with it the benefit of Active Scripting – you can make use of JScript, VBScript, PerlScript or your other favorite Active Scripting language in an XSLT stylesheet.
We have to add the namespace declaration msxsl to the stylesheet header and the namespace to identify our user scripts. This does have the disadvantage that non–Microsoft processors, such as Saxon and XT, will be unable to process XSLT stylesheets with script content. This is not a failing on their part – far from it. Microsoft have extended their implementation of XSLT to allow Active Scripting, thus if we can guarantee their processor will be installed, we may as well use all the features.
Thus a stylesheet header may look like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="urn:cm-script-blocks" version="1.0">
The syntax of the msxsl:script element is:
<msxsl:script language = "language-name" implements-prefix = "prefix of user's namespace"> </msxsl:script>
If no language attribute is specified, JScript is the default. Any script that we write needs to be enclosed within the <msxsl:script> and </msxsl:script> tags. A wise precaution is to include the script inside the CDATA section, i.e. within the <![CDATA[ and ]]> tags – after all, we don’t want our script being recognized as XML or XSL!
The implements–prefix attribute is compulsory – it’s a namespace that uniquely identifies our script. The relationship between the msxsl namespace and the user’s namespace is best described graphically:

Here’s the full XSLT stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope"
xmlns:m="http://www.craigmurphy.com/currency"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="urn:cm-script-blocks"
version="1.0">
<xsl:output method="xml" encoding="UTF-8" />
<xsl:template match="//SOAP-ENV:Body">
<!-- Get the currency amount and store it in a variable -->
<xsl:variable name="amount">
<xsl:value-of select="//SterlingAmount" />
/xsl:variable>
<!-- Get the currency id and store it in a variable -->
<xsl:variable name="currency_id">
<xsl:value-of select="//currencyID" />
</xsl:variable>
<!-- Get the rate the the currency specified by currency_id -->
<xsl:variable name="rate">
<xsl:value-of select="user:getRate(string($currency_id))"/>
</xsl:variable>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope">
<SOAP-ENV:Body>
<m:getCurrencyResponse xmlns:m="http://www.craigmurphy.com/currency">
<result>
<!-- Calculate the foreign currency amount -->
<xsl:value-of select="format-number($amount * $rate, '#.00')" />
</result>
</m:getCurrencyResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</xsl:template>
<msxsl:script implements-prefix="user"><![CDATA[
function getRate(sCurrID) {
var Source;
var Connect;
var Rs;
var fRate;
Source = "SELECT * FROM tblCurrencies WHERE id=" + sCurrID;
Connect = "DSN=ASPToday;UID=;PWD=;";
Rs = new ActiveXObject("ADODB.Recordset");
Rs.Open( Source, Connect );
fRate = Rs("Rate").value;
Rs.Close();
return fRate;
}
]]></msxsl:script>
</xsl:stylesheet>
Our stylesheet contains a template that matches the Body element of the SOAP Request. It then extracts SterlingAmount and currencyID into variables (more about these later in this article). Using the currencyID, a piece of JScript is executed – namely the function getRate(...) . A SOAP Request is then built up, with a formula to calculate the correct currency conversion. We’ll see the SOAP Response in just a moment.
That’s a lot in a short space. Let’s go through a few of the more salient points.
As you’ve probably noticed, it’s possible to create instances of COM components inside the msxsl:script element, so we can still perform our database access using ADO. This is one of the strengths of the Microsoft implementation – we are now able to create self–contained stylesheets that respond to changes in underlying databases. Our web–based content becomes very dynamic. It’s one thing being able to create XML from a database, but now we have the mechanism to use a database to assist in our presentation and transformation of the XML. Whilst some might say this is a bad thing, as long as we are careful not to try to do too much with a stylesheet by overloading it with functionality, then we’ll reap the rewards.
The addition of namespaces to the stylesheet element is compulsory. We are creating XML that uses each of the namespaces (the SOAP Response), so they must be present or the XML parser/XSLT processor wouldn’t be able to successfully perform the transform. The stylesheet element below shows all the namespaces that we need:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope" xmlns:m="http://www.craigmurphy.com/currency" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="urn:cm-script-blocks" version="1.0">
I’ve used variables to extract nodes from the SOAP request. The <xsl:value-of> element takes a select attribute, which gives us access to the XML input document – the SOAP request:
<!-- Get the currency id and store it as a variable --> <xsl:variable name="currency_id"> <xsl:value-of select="//currencyID" /> </xsl:variable>
Executing our "user" scripting functions requires us to prefix the function name with the namespace prefix for our user scripts – in our case this is user . After the prefix, the function can be used as if it were normal JScript, VBScript, etc. It’s possible to make use of the XSLT functions too – I’ve used the string() function to ensure that getRate(...) receives a string:
<!-- Get the rate the the currency specified by currency_id --> <xsl:variable name="rate"> <xsl:value-of select="user:getRate(string($currency_id))"/> </xsl:variable>
You can see how variables are used in XSLT – first they are assigned using the <xsl:variable> element, then they are accessed by prefixing the name with a $ (dollar) sign.
The original "Sterling amount" is held in the variable amount . The getRate() function has already returned a suitable conversion rate for us, in the variable rate . The "Converted amount" is nothing more than the product of amount and rate. Purely for completeness, the final "Converted amount" is formatted neatly using the format-number function:
<xsl:value-of select="format-number($amount * $rate, '#.00')" />
Instead of executing methods written using JScript, VBScript or your favorite high–level language development tool, we’re going to load the Body of the SOAP Request into a new XMLDOM. We’ll also load the calcCurrency.xsl stylesheet into an XMLDOM too. Here’s the JavaScript that identifies the getCurrency method then performs the transform:
case "getCurrency":
data = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
data.async = false;
data.loadXML(oBody.xml);
style = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
style.async = false;
style.setProperty ("SelectionLanguage", "XPath");
if (style.load(Server.MapPath("calcCurrency.xsl"))==false)
Response.Write (style.parseError.errorCode + " " + style.parseError.reason + "\n");
var xsltemp = new ActiveXObject("MSXML2.XSLTemplate");
xsltemp.stylesheet = style;
var xslproc = xsltemp.createProcessor();
xslproc.input = data;
xslproc.transform();
var re = xslproc.output;
Response.Write(re);
break;
If all went well, the SOAP Response should look like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope"> <SOAP-ENV:Body> <m:getCurrencyResponse xmlns:m="http://www.craigmurphy.com/currency"> <result>236700</result> </m:getCurrencyResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
As an enhancement we could easily have included the conversion rate that was used – giving the user extra feedback is always a good idea.
The code for the getCurrency method also makes use of Microsoft–specific functionality for transforming XML using XSL. You probably noticed that we created an instance of MSXML2.XSLTemplate , then assigned our stylesheet to xsltemp.stylesheet . Using XSLTemplate offers performance gains – we could store xsltemp in an ASP Application variable, and then reuse the template over and over again. The stylesheet associated with xsltemp has already been compiled, so reusing a compiled stylesheet improves performance.
Another benefit of XSLTemplate is the ability to pass parameters from, say, our ASP script into the XSLT stylesheet. Here’s how we could add a currentRate parameter to an XSLT stylesheet:
<xsl:param name="currentRate" />
Stylesheet–wide parameters are typically declared after the initial <xsl:stylesheet ...> element.
Setting the parameter value using JScript in ASP requires us to create an instance of the XSLT processor for the cached stylesheet:
var xslproc = xsltemp.createProcessor(); xslproc.input = data;
We can then call the addParameter method to pass data/information into the stylesheet:
xslproc.addParameter("currentRate", 1.2);
xslproc.transform();
Response.Write(xslproc.output);
Using the parameter in an XSLT stylesheet is the same as using an XSLT variable: $currentRate .
Whilst I’ve passed in 1.2 as a value for currentRate , we could equally well have queried a database to get the value, thus negating the need for script in the XSLT. Like most things in software development, there’s more than one way to accomplish the same thing!
Over the last three articles, we’ve seen how SOAP can be used to implement service–oriented processes.
SOAP can also assist us in a number of areas:
During June 2000 Microsoft released the first version of their SOAP Toolkit (the latest version as of publication is from July 2000). The SOAP Toolkit provides a DLL based architecture for implementing SOAP on the client and the server. It also ships with a wizard that is able to create a SOAP listener for an existing COM component, and it creates the Service Description Language (SDL) file that I mentioned in part 2. Unfortunately, the SOAP Toolkit requires a DLL to be installed on the client machine – thus restricting use to Win32 platforms. The techniques I’ve presented over the course of these articles, whilst relying on Internet Explorer 5.x, provide a foundation for portability. There’s no reason why the client side JavaScript couldn’t be ported to work on other platforms – provided support for XML and HTTP is available.
|
| |||||||
| |||||||||||||||
|
| ASPToday is brought to you by
Wrox Press (http://www.wrox.com/). Please see our terms
and conditions and privacy
policy. ASPToday is optimised for Microsoft Internet Explorer 5 browsers. Please report any website problems to webmaster@asptoday.com. Copyright © 2001 Wrox Press. All Rights Reserved. |