Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Web Service Proxy generator using XSLT targeting VBScript and JavaScript

0.00/5 (No votes)
3 Jul 2007 1  
Describes the use of an XSLT stylesheet to transform a WSDL definition of a web service to VBScript/JavaScript proxy code

Introduction

In this article a simple code generator for generating web proxy classes targeting JavaScript/VBScript is described. The code generator is essentially a single XSLT document.

Background

Whilst working with a web application that made heavy use of VBScript I wanted a simple way to access web services (written in C#). As I couldn't find a web service generation tool for VBScript I wrote my own, using XSLT. Generating equivalent JavaScript was not difficult, so I included generation to JavaScript as well. This may be useful for programmers who wish to call web services from JavaScript but do not want to use a full-blown AJAX framework. In fact, the approach I adopted is almost anti-AJAX - it's synchronous, and it uses string handling routines to create/parse the web request/response.

Using the code

To generate VBScript web proxy code, an XSLT transformation needs to be performed. This can be achieved in a few lines of code:

//

// Transform wsdl

//


XslCompiledTransform xsltransform = new XslCompiledTransform();

try
{
    string xsltFileName = "WSDLtoVBScript.xslt";
    string wsdlSourceFileName = "ServiceX.wsdl";
    string vbscriptProxyFileName = "ServiceX.vbs";

    xsltransform.Load(xsltFileName);
    xsltransform.Transform(wsdlSourceFileName, vbscriptProxyFileName);
}
catch (System.Xml.XmlException theException)
{
    // Handle exception

}

An HTML file which contains VBScript client code to access the web service would look as follows:

<html>
    <head>
        <script language="vbscript" src="./Scripts/String.vbs"></script>
        <script language="vbscript" src="./Scripts/ServiceXProxy.vbs"></script>

        <script language="vbscript">
        Dim ServiceXProxy

        Sub window_onload
            Set ServiceXWS = New ServiceX
            ServiceXWS.serviceAddress = "htpp://myhost.com/ServiceX.asmx"
        End Sub

        Sub Test
            window.alert(ServiceX.Test1)
        End Sub
        </script>
    </head>
    <body>
    ...
        <input type=button value="Test" id="TestBtn" onclick="vbscript:Test()" />
    ...
    </body>
</html>

The above example assumes that the web service ServiceX has a method called Test1 which returns a string value. Also, the file String.vbs needs to be included as it contains string handling routines required by the web proxy class.

XSLT Stylesheet

The XSLT will generate a class with the same name as the web service (as specified by the name attribute of the service WSDL element). Within the class a function is defined for each method (i.e. each operation WSDL element). Prior to this, new classes are defined for each new type defined by the web service, and for each new type a SOAPResponseTo_(name of type) function is generated which parses a soap response and returns a VBScript instance of the type. Thus the structure of the WSDLtoVBScript.xslt document is as follows:

  • Generate class definitions of complex types (/wsdl:definitions/wsdl:types/s:schema/s:complexType[not(contains(@name,'ArrayOf'))])
  • Generate SOAP response parsing methods - for simple types and complex types (wsdl:definitions/wsdl:types/s:schema/s:complexType)
  • Generate the web service class (/wsdl:definitions/wsdl:service)
  • Generated the function definitions (/wsdl:operation)

This is described in more detail below.

Set output type

As the output will be VBScript/JavaScript code the output type is text:

<xsl:output method="text" />

Declare global variables

Some commonly used element values are given variable names for quick access in later parts of the script, for instance, the serviceName variable is defined as follows:

<xsl:variable name="serviceName" 
        select="/wsdl:definitions/wsdl:service/@name"/>

Select root node

The root node is selected to begin the transformation:

<xsl:template match="/">

Class definitions

A VBScript class is created for each complex type (which isn't an array), using the xsl:for-each element to loop through all the applicable complex types:

<xsl:for-each select="/wsdl:definitions/wsdl:types/s:schema/s:
                complexType[not(contains(@name,'ArrayOf'))]">
...
</xsl:for-each>

The class definition also includes a toString function, which uses a common XSLT 'pattern' to generate a comma separated list of values:

str = str + "<xsl:if test="position()!=1">, </xsl:if>...

SOAP response parsing methods

No actual transformation is required for the parsing of simple SOAP response types, so the methods have been included as plain text (they could be taken out and moved to an include script). Eg. for integer types:

Function SOAPResponseTo_int(soapRespText)
    SOAPResponseTo_int = CInt(soapRespText)
End Function

For complex types the same for-each loop is used as for the initial class definitions for complex types. A check is made using the xsl:choose element to see whether the complex type is an array or not:

<xsl:for-each select="wsdl:definitions/wsdl:types/s:schema/s:complexType">
<xsl:variable name="baseClass" 
    select="substring-after(s:complexContent/s:extension/@base,':')"/>
Function SOAPResponseTo_<xsl:value-of select="@name"/>(soapRespText)
    <xsl:choose>
        <xsl:when test="contains(@name,'ArrayOf')">
        ...
        </xsl:when>
        <xsl:otherwise>
        ...
        </xsl:otherwise>
    </xsl:choose>
End Function
</xsl:for-each>

Generate class

The web service proxy class is generated as follows:

Class <xsl:value-of select="$serviceName"/>
    Public serviceAddress
    Public serviceNamespace

    Private Sub Class_Initialize
        me.serviceAddress
            = "<xsl:value-of select=_
     "/wsdl:definitions/wsdl:service/wsdl:port/soap:address/@location" />"
        me.serviceNamespace
            = "<xsl:value-of select="$serviceNamespace"/>"
    End Sub

    <xsl:apply-templates select=_
    "wsdl:definitions/wsdl:binding[soap:binding]/wsdl:operation" />

End Class

This is the last text inside the root xsl:template node. The xsl:apply-templates element will result in the wsdl:operation elements being added to the stack of nodes for the XSLT processor to process.

Generate functions

Finally the functions are generated by template matches on the wsdl:operation elements:

<xsl:template match="wsdl:operation">
    <xsl:variable name="methodName" select="@name" />
    <xsl:variable name="resultType" select=... />

' Method: <xsl:value-of select="$methodName"/> 
        (for operation '<xsl:value-of select="@name"/>')
' ...

Public Function <xsl:value-of select="$methodName"/>(
    <xsl:call-template
        name="CallParameterList">
        <xsl:with-param name="methodName" select="$methodName"/>
    </xsl:call-template>)
    ' Create the XML HTTP request object
    Set req = CreateObject("Microsoft.XMLHTTP")

    ' Construct the SOAP request
    Dim soapReq
    soapReq = ...

    ' Send the SOAP request (synchronously)
    req.open "POST", me.serviceAddress, false
    req.setRequestHeader "Content-Type", "text/xml; charset=utf-8"
    req.setRequestHeader "SOAPAction", me.serviceNamespace + 
                "<xsl:value-of select="$methodName"/>"
    req.send soapReq

    ' Construct the result
    <xsl:choose>
        <!-- Simple type result -->
        <xsl:when test="($resultType = 's:int') or 
            ($resultType = 's:string') or ...">
            <xsl:value-of select="$methodName"/>
                = SOAPResponseTo_<xsl:value-of select=
                "substring-after($resultType,':')"/>(...)

        </xsl:when>
        ...
    </xsl:choose>
End Function
</xsl:template>

The signature of the function is generated by making a call to an XSLT named template (like a procedure - see below) via the xsl:call-template element. The SOAP request is constructed, then posted to the web service address using the Microsoft.XMLHTTP object. The SOAP response is then deserialised by making a call to the appropriate SOAP_ResponseTo_... function.

Named templates (XSLT procedures)

The function generated XSLT above makes much use of calls to named templates to break up the code better. An example is the CallParameterList named template:

<xsl:template name="CallParameterList">
    <xsl:param name="methodName"/>
    <xsl:for-each
        select="/wsdl:definitions/wsdl:types/s:schema/s:element
        [@name=$methodName]/s:complexType/s:sequence/s:element">
        <xsl:if test="position()!=1">, </xsl:if><xsl:value-of select="@name" />
    </xsl:for-each>
</xsl:template>

JavaScript

For JavaScript the process is much the same. The file String.js needs to be included in place of String.vbs. Also, the prototype feature of JavaScript is used instead of the Class construct in VBScript.

History

  • 27 Jun 2007 - Posted
  • 04 Jul 2007 - Described XSLT Stylesheet in more detail

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here