This documentation is archived and is not being maintained.

Using the Office XP Web Services Toolkit 2.0 to Interact with Complex Types in XML Web Services

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.
 

Frank C. Rice
Microsoft Corporation

July 2002

Applies to:
    Microsoft® Office XP Web Services Toolkit 2.0
    Microsoft SOAP Toolkit 3.0

Summary: Learn how to use simple and complex data types in XML Web services by using the SOAP 3.0 DLL and the Office XP Web Services Toolkit's, Web Service References Tool 2.0. (31 printed pages)

Download setup.exe.

Contents

Introduction
The SOAP 3.0 DLL
The Web Service References Toolkit 2.0
Using the Web Service References Tool 2.0
The SalesRankNPrice XML Web Service
Creating the Proxy Class
Examining the Proxy Class Code
The All User-Defined Complex Data Type
Calling the SalesRankNPrice XML Web Service
Using Simple Data Types
Using XML as a Complex Data Type
Using Arrays as Complex Data Types
Using Enumerations as Complex Data Types
Conclusion

Introduction

Extensible Markup Language (XML) Web services are typically comprised of a client application that sends Simple Object Access Protocol (SOAP) messages containing input parameters to a Web server application. These messages are sent using the Hypertext Transfer Protocol (HTTP) in order to invoke the XML Web service. The results of the XML Web service are then returned to the client, also inside of a SOAP message.

The data contained in these SOAP messages can consist of simple or complex data types, as specified in a Web Services Description Language (WSDL) file located on the Web server. Simple data types typically include a single value containing a String value, Integer value, or other unary data type. Complex data types can consist of structures containing a combination of simple data types such as arrays, user-defined types (UDTs), enumerations, or a list of XML nodes.

Note   The WSDL file identifies information about the services provided by the XML Web service to the client application such as the names of the Web methods and the parameters that make up the input and output.

To assist you in creating and consuming XML Web services, the Microsoft® SOAP 3.0 dynamic-link library (DLL) and the Microsoft Office XP Web Services Toolkit 2.0 each contain a number of features which will be explored in this article.

This article describes to how use simple and complex data types with XML Web services by using the SOAP 3.0 DLL and the Web Service References Tool 2.0, both of which are provided with the Office XP Web Services Toolkit 2.0.

The SOAP 3.0 DLL

To assist you in working with simple and complex data types in XML Web services, the SOAP 3.0 DLL provides a high-level application programming interface (API) that exposes both a client-side and a server-side component.

Note   In addition to being provided with the Office XP Web Services Toolkit 2.0, the SOAP 3.0 DLL is also included in the SOAP Toolkit 3.0 along with a user guide.

The SoapClient30 object allows a client application to invoke an XML Web service operation just as if the client resided on the Web server. The SoapServer30 object provides access to the methods in the XML Web service by mapping each XML Web service method to corresponding Component Object Model (COM) object method calls, as described by the WSDL file and the Web Services Meta Language (WSML) file.

Note   A WSML file maps the methods of the XML Web service (as described in the WSDL file) to specific methods in the COM object, which implements the XML Web service. The WSML file is only necessary for the implementation of the Microsoft SOAP Toolkit.

The SoapClient30 and SoapServer30 objects can recognize and automatically handle simple data types. However, they do not recognize or know how to handle complex data types. As a result, in order to use these objects with complex data types, you must explicitly convert your complex data types to sets of simple data types which the objects can understand. To do this, the SOAP 3.0 DLL provides type mappers for COM objects or UDTs, and the IXMLDOMNodeList interface for XML data.

Type mappers map complex types to COM objects by both deserializing SOAP messages (reading the incoming SOAP messages and building COM objects for the various complex data types) and serializing these COM objects (writing the COM objects to an outgoing SOAP message).

Note   Serialization is the process of converting an object into a form that can be readily transported. For example, you can serialize an object and transport it over the Internet using HTTP between a client and a server. On the other end, deserialization reconstructs the object from the stream.

You can use the following type mappers and interfaces from the SOAP 3.0 DLL:

  • A generic type mapper to map complex data types to COM methods when COM objects are used to implement the XML Web service.
  • A user-defined data type mapper (UDT Mapper) to map complex data types to method calls when UDTs are used instead of COM objects.
  • A custom type mapper that lets you create custom data type mappings. The SOAP 3.0 DLL includes a SoapTypeMapperFactory30 object, which can be used to create custom type mappings for simple data types, arrays, and registered complex types.
  • An IXMLDOMNodeList interface to map complex types to an XML Document Object Model (DOM) list of nodes.

The SOAP 3.0 DLL also provides low-level interfaces which allow the client and server to generate, build, exchange, and process SOAP messages. These low-level interfaces allow you to create your own SOAP servers and clients instead of relying on the SoapClient30 and SoapServer30 objects found in the high-level API.

Additionally, the SOAP Toolkit 3.0 also provides other tools and utilities to assist you in working with XML Web services, which include:

  • The WSDL/WSML Generator tool that generates the WSDL and WSML files for you
  • The TCP/IP trace utility, MSSOAPT.EXE, which you can use to view the SOAP messages sent by HTTP between a SOAP client and an XML Web service on the Web server

More detailed information about type mappers, the IXMLDOMNodeList interface, and other tools can be found in the help topics contained in the SOAP Toolkit 3.0.

The Web Service References Tool 2.0

In addition to the features provided by the SOAP 3.0 DLL, the Office XP Web Services Toolkit 2.0 provides the Web Service References Tool 2.0 to help you access and utilize XML Web services. The Web Service References Tool 2.0 is used in a client application to create a Microsoft Visual Basic® for Applications (VBA) proxy class module from a WSDL file or Microsoft Visual Studio® .NET discovery (.vsdisco) file. This proxy class contains methods and other code that allows you to invoke the XML Web service methods just as if the client application resided on the Web server.

Specifically, the Web Service References Tool 2.0 provides the following features:

  • A user interface to assist in finding, testing, and selecting XML Web services.
  • A proxy object in the client application with allows direct calls to the objects and methods on the Web server. These objects are contained in a class file that starts with the prefix clsws_ followed by the XML Web service name.
  • A class that defines any UDTs, as described in the WSDL file. These are contained in a class file that starts with the prefix struct_.
  • A class that provides a mapping between the XML IXMLDOMNode interface returned by an XML Web service in a UDT and a data structure created by the Web Service References Tool 2.0. SOAP uses this mapping to format the XML so that it can be used by VBA. These class files are named clsof_Factory_ followed the name of the XML Web service.

As we'll see, using complex data types with XML Web services is relatively straightforward with the Web Service References Tool 2.0.

Using the Web Service References Tool 2.0

To use the Web Service References Tool 2.0, you start by clicking Web Service References from the Tools menu in the Visual Basic Editor (VBE) in an Office application (such as Microsoft Access, Microsoft Excel, Microsoft FrontPage®, or Microsoft Outlook®). This displays a user interface that you can use to search for the WSDL (or .vsdisco) file, which contains the methods that define the XML Web service (see Figure 1). This file can be located by searching a Universal Description, Discovery and Integration (UDDI) business registry or by providing a uniform resource locator (URL) directly to the file. UDDI registries can be searched by company name or by type of XML Web service.

Once you have located and set a reference to the WSDL file in the VBE, the Web Service References Tool 2.0 reads the file and determines the XML Web service's method names and parameter information. The tool then uses this information to create a proxy class and other class files in the client application. The proxy class can be used to declare and instantiate objects that access the XML Web service, just as you would use any other type of intrinsic Office object or method. In addition, because the objects and methods are available to the client application at design time, you get the added benefit of Microsoft IntelliSense® and syntax checking against XML Web services (also known as early binding).

The proxy class also handles the details of sending requests and receiving responses from the XML Web service. For example, when the client application is ready to use the XML Web service, the proxy class creates a SOAP message containing the method information and then passes the SOAP packet to the Web server using HTTP. Once the request has been processed on the server and returned to the client, the proxy class intercepts the response and passes the returned value to the client application where it is parsed and processed.

Without the Web Service References Tool 2.0, you would need to go through a series of complicated and error-prone steps to call an XML Web service from an Office solution. For example, you would need to know the URL of the XML Web service's WSDL file in order to retrieve the XML Web service's method names and input and output parameters. In addition, because the client application wouldn't have access to the objects and methods of the XML Web service until run time, early binding isn't possible, so you wouldn't have the benefit of IntelliSense or syntax checking.

The remainder of this article demonstrates accessing XML Web services that use single and complex data types with the Web Service References Tool 2.0. For part of this demonstration, we will use the SalesRankNPrice and Zip Code Resolver XML Web services. For other explanations, we will use hypothetical XML Web services.

The SalesRankNPrice XML Web Service

The SalesRankNPrice XML Web service can be used to retrieve the sales ranking and price for any book available on the Amazon or Barnes and Noble Web sites, as referred to by its International Standard Book Number (ISBN). Among the several methods exposed by this XML Web service are the following, which we will use to explore using the Web Service References Tool 2.0:

  • GetBNPrice This Web method accepts an ISBN String value and returns BarnesAndNoble.com Price (in US Dollars) for that book as a String value.
  • GetAll This Web method accepts an ISBN String value and returns the Amazon.com and BarnesAndNoble.com sales ranking as well as Amazon.com and BarnesAndNoble.com prices (in USD) for that book as a struct_All user-defined structure. The returned data is one structure containing four String values (AmazonSalesRank, AmazonPrice, BNSalesRank, and BNPrice).

Let's look at the code generated by the Web Service References Tool 2.0 after setting a reference to the SalesRankNPrice WSDL file.

Creating the Proxy Class

When you create a reference to a WSDL file in the VBE by clicking Web Service References from the Tools menu, the following screen appears:

Aa140321.odc_cmplxtypes01(en-us,office.10).gif

Figure 1. The Web Service References Tool user interface

The Web Service References dialog box contains the following areas:

  • The Web Service Search area allows you to perform an XML Web service search using keywords or a business name against a UDDI Business Registry.
  • The Web Service URL area allows you to perform an XML Web service search using a specific URL to a WSDL or .vsdisco file.
  • The Search Results area allows you to select available XML Web services for which you want the tool to create proxy classes.
  • The Test button allows you to test an XML Web method that you select in the Search Results area (if the XML Web service administrator has provided a corresponding .asmx test harness page).

Once you select an XML Web service and click the Add button, the Web Service References Tool 2.0:

  • Creates a class module containing a structure that maps to the complex data types of the service, assuming the XML Web service contains at least one method that uses complex data types. Each structure class file begins with struct_ followed by the user-defined data type name as defined in the WSDL file. For example, after setting a reference to the SaleRankNPrice XML Web service, the class struct_All is created to contain the user-define type used in the GetAll Web method.
  • Creates an object factory class file, which provides an interface to the SOAP 3.0 DLL Object Factory if the XML Web service contains a method that uses complex data types. The object factory class is used to create a mapping between a type mapper on the server and any user-defined types on the client. User-defined types are returned from the XML Web service as XML documents using the IXMLDOMNode interface. The class file begins with clsof_Factory followed by the name of the XML Web service.
  • Creates one XML Web service class file for each selected XML Web service regardless of the data types the XML Web service implements. This class file begins with the prefix clsws_ followed by the XML Web service's name. For instance, if you add the SalesRankNPrice XML Web service to a project, the clsws_SalesRankNPrice class file is created.
  • Creates one Private SoapClient30 object variable in the XML Web service class file which corresponds to an instance of the XML Web service. This object variable begins with the prefix sc_ followed by the XML Web service's name. For the SalesRankNPrice XML Web service, this object variable is named sc_SalesRankNPrice.
  • Creates one Private constant in the XML Web service class file that contains the URL to the XML Web service's WSDL file. This constant is always named c_WSDL_URL.
  • Creates one Private constant in the XML Web service class file that corresponds to the name of XML Web service as specified in the WSDL file. This constant is always named c_SERVICE. For the SalesRankNPrice XML Web service, this constant contains the value SalesRankNPrice.
  • Creates one Private constant in the XML Web service class file that corresponds to the port of the XML Web service where the SOAP messages are received as specified in the WSDL file. This constant is always named c_PORT. For the SalesRankNPrice XML Web service, this constant contains the value SalesRankNPriceSoap.
  • Creates one Private constant in the XML Web service class file that corresponds to the namespace of XML Web service as specified in the WSDL file. This constant is always named c_SERVICE_NAMESPACE. For the SalesRankNPrice XML Web service, this constant contains the value http://www.PerfectXML.com/NETWebSvcs/BookService.
  • Creates one concatenated String variable in the XML Web service class file named str_WSML. If the XML Web service contains a complex data type, the str_WSML constant contains a concatenated String value which links the complex data type defined in the WSDL file to a user-defined structure in the client application. If no complex data type is defined, this variable contains a Null value. For the SalesRankNPrice XML Web service, this constant contains the value:
        Dim str_WSML As String
        str_WSML = "<servicemapping name='SalesRankNPrice'>"
        str_WSML = str_WSML & "<service name='SalesRankNPrice'>"
        str_WSML = str_WSML & "<using 
          PROGID='MSSOAP.GenericCustomTypeMapper30' cachable='0' 
          ID='GCTM'/>"
        str_WSML = str_WSML & "<types>"
        str_WSML = str_WSML & "<type name='All' 
          targetNamespace='http://www.PerfectXML.com/
          NETWebSvcs/BookService' uses='GCTM' 
          targetClassName='struct_All'/>"
        str_WSML = str_WSML & "<type name='Prices'
          targetNamespace='http://www.PerfectXML.com/
          NETWebSvcs/BookService' uses='GCTM' 
          targetClassName='struct_Prices'/>"
        str_WSML = str_WSML & "<type name='SalesRankNPrice1'
          targetNamespace='http://www.PerfectXML.com/
          NETWebSvcs/BookService' uses='GCTM' 
          targetClassName='struct_SalesRankNPrice1'/>"
        str_WSML = str_WSML & "<type name='SalesRanks' 
          targetNamespace='http://www.PerfectXML.com/
          NETWebSvcs/BookService' uses='GCTM' 
          targetClassName='struct_SalesRanks'/>"
        str_WSML = str_WSML & "</types>"
        str_WSML = str_WSML & "</service>"
        str_WSML = str_WSML & "</servicemapping>"
    
    
  • Creates one Public function in the XML Web service class file for each exposed Web method in the XML Web service. The function always begins with wsm_ followed by the Web method's name and corresponding input and output parameters (since parameters are sent and received using SOAP, the Web Service References Tool 2.0 must translate SOAP data types to simple VBA data types). For the GetAll Web method of the SalesRankNPrice XML Web service, the corresponding function is named wsm_GetAll.

Examining the Proxy Class Code

Let's examine the proxy class code generated by the Web Service References Tool 2.0 for the SalesRankNPrice XML Web service in greater detail.

When we set the reference to the WSDL file, the tool generates a class module clsws_SalesRankNPrice containing the code listed below. The first section provides comments about how this code was created, followed by instructions on how to declare and instantiate this class:

'*****************************************************************
'This class was created by the Web Service References Tool 2.0.
'
'Created: 4/30/2002 09:08:09 AM
'
'Description:
'This class is a Visual Basic for Applications class representation of
  the Web service
'as defined by http://www.perfectxml.net/WebServices/
  SalesRankNPrice/BookService.asmx?wsdl.
'
'To Use:
'Dimension a variable as new clsws_SalesRankNPrice, and then write code
  to
'use the methods provided by the class.
'Example:
' Dim ExampleVar as New clsws_SalesRankNPrice
' debug.print ExampleVar.wsm_GetAmazonSalesRank("Sample Input")
'
'For more information, see Complex Types in Web Service References
'Tool 2.0 Help.
'
'Changes to the code in this class may result in incorrect behavior.
'
'*****************************************************************

Next, two internal class variables are dimensioned, representing the SoapClient30 object and the URL to the XML Web service's WSDL file, respectively. The SoapClient30 object always starts with the prefix sc_ followed by the XML Web service name. The constant c_WSDL_URL will always have the same name regardless of the class file.

...
' Dimensioning private class variables.
Private sc_SalesRankNPrice As SoapClient30
Private Const c_WSDL_URL As String = 
  "http://www.perfectxml.net/WebServices/
  SalesRankNPrice/BookService.asmx?wsdl"
...
Then, other variables are declared, representing the name of the
  service, the port that receives the SOAP requests, and the XML Web
  service namespace, respectively.
...
Private Const c_SERVICE As String = "SalesRankNPrice"
Private Const c_PORT As String = "SalesRankNPriceSoap"
Private Const c_SERVICE_NAMESPACE As String =
  "http://www.PerfectXML.com/NETWebSvcs/BookService"
...

The Class_Initialize event procedure first declares a concatenated String variable, which maps the XML Web service's, All complex data type (used with the GetAll Web method) to the proxy structure defined in the file struct_All, as str_WSML.

Private Sub Class_Initialize()
    '*****************************************************************
    'This subroutine will be called each time the class is
      instantiated.
    'Creates sc_ComplexTypes as new SoapClient30, and then
    'initializes sc_ComplexTypes.mssoapinit2 with WSDL file found in
    'http://www.perfectxml.net/WebServices/
      SalesRankNPrice/BookService.asmx?wsdl.
    '*****************************************************************

    Dim str_WSML As String
    str_WSML = "<servicemapping name='SalesRankNPrice'>"
    str_WSML = str_WSML & "<service name='SalesRankNPrice'>"
    str_WSML = str_WSML & "<using 
      PROGID='MSSOAP.GenericCustomTypeMapper30' cachable='0'
      ID='GCTM'/>"
    str_WSML = str_WSML & "<types>"
    str_WSML = str_WSML & "<type name='All'
      targetNamespace='http://www.PerfectXML.com/
      NETWebSvcs/BookService' uses='GCTM' 
      targetClassName='struct_All'/>"
    str_WSML = str_WSML & "<type name='Prices'
      targetNamespace='http://www.PerfectXML.com/
      NETWebSvcs/BookService' uses='GCTM' 
      targetClassName='struct_Prices'/>"
    str_WSML = str_WSML & "<type name='SalesRankNPrice1'
      targetNamespace='http://www.PerfectXML.com/
      NETWebSvcs/BookService' uses='GCTM' 
      targetClassName='struct_SalesRankNPrice1'/>"
    str_WSML = str_WSML & "<type name='SalesRanks'
      targetNamespace='http://www.PerfectXML.com/
      NETWebSvcs/BookService' uses='GCTM'
      targetClassName='struct_SalesRanks'/>"
    str_WSML = str_WSML & "</types>"
    str_WSML = str_WSML & "</service>"
    str_WSML = str_WSML & "</servicemapping>"

    Set sc_SalesRankNPrice = New SoapClient30

    sc_SalesRankNPrice.MSSoapInit2 c_WSDL_URL, str_WSML, c_SERVICE,
      c_PORT, c_SERVICE_NAMESPACE
    'Use the proxy server defined in Internet Explorer's LAN settings
      by
    'setting ProxyServer to <CURRENT_USER>
    sc_SalesRankNPrice.ConnectorProperty("ProxyServer") =
      "<CURRENT_USER>"
    'Autodetect proxy settings if Internet Explorer is set to
      autodetect
    'by setting EnableAutoProxy to True
    sc_SalesRankNPrice.ConnectorProperty("EnableAutoProxy") = True

    Set sc_SalesRankNPrice.ClientProperty("GCTMObjectFactory") = New
      clsof_Factory_SalesRankNPri
End Sub

The All complex data type is defined in the SalesRankNPrice XML Web service WSDL file with the following code:

...
<s:element name="GetAll">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="ISBN"
              type="s:string" /> 
        </s:sequence>
    </s:complexType>
</s:element>
<s:element name="GetAllResponse">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetAllResult"
              type="s0:All" />
        </s:sequence>
    </s:complexType>
</s:element>
<s:complexType name="All">
    <s:sequence>
        <s:element minOccurs="0" maxOccurs="1" name="AmazonSalesRank"
          type="s:string" />
        <s:element minOccurs="0" maxOccurs="1" name="AmazonPrice"
          type="s:string" />
        <s:element minOccurs="0" maxOccurs="1" name="BNSalesRank"
          type="s:string" />
        <s:element minOccurs="0" maxOccurs="1" name="BNPrice"
          type="s:string" />
    </s:sequence>
</s:complexType>
...

The Class_Initialize event procedure then uses the MSSoapInit2 method of the SoapClient30 proxy object to instantiate the sc_SalesRankNPrice object. The ClientProperty method of the SoapClient30 object is then used to create a new instance of the clsof_Factory_SalesRankNPri class.

The Class_Terminate event is used to clean up resources that are no longer needed, namely, the SoapClient30 proxy object.

Private Sub Class_Terminate()
    '*****************************************************************
    'This subroutine will be called each time the class is destructed.
    'Sets sc_ComplexTypes to Nothing.
    '*****************************************************************

    'Error Trap
    On Error GoTo Class_TerminateTrap

    Set sc_SalesRankNPrice = Nothing

Exit Sub

Class_TerminateTrap:
    SalesRankNPriceErrorHandler ("Class_Terminate")
End Sub

The SalesRankNPriceErrorHandler subroutine is used to handle any errors raised by the Web server hosting the XML Web service. This subroutine's name is always prefixed by the XML Web service name followed by the text ErrorHandler.

Private Sub SalesRankNPriceErrorHandler(str_Function As String)
    '*****************************************************************
    'This subroutine is the class error handler. It can be called from 
      any class subroutine or function
    'when that subroutine or function encounters an error. Then, it 
      will raise the error along with the
    'name of the calling subroutine or function.
    '*****************************************************************

    'SOAP Error
    If sc_SalesRankNPrice.faultcode <> "" Then
        Err.Raise vbObjectError, str_Function,
          sc_SalesRankNPrice.faultstring
    'Non SOAP Error
    Else
        Err.Raise Err.Number, str_Function, Err.Description
    End If
End Sub

Finally, proxy methods corresponding to the XML Web service's methods are generated. When you select the SalesRankNPrice XML Web service in the Web Service References dialog box, you get all of the methods that are available for this service. In this section, we will only discuss the code for the GetAll Web method that uses complex data types. You should note that Web method signatures created by the Web Service References Tool 2.0:

  • Are always declared as Public Function
  • Are always prefixed by the text wsm_ followed by the Web method name
  • List input parameters with a data type prefix followed by the Web method parameter

Here's the generated code:

Public Function wsm_GetAll(ByVal str_ISBN As String) As struct_All
    '*****************************************************************
    'Proxy function created from 
      http://www.perfectxml.net/WebServices/
      SalesRankNPrice/BookService.asmx?wsdl.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetAllTrap

    Set wsm_GetAll = sc_SalesRankNPrice.GetAll(str_ISBN)

Exit Function
wsm_GetAllTrap:
    SalesRankNPriceErrorHandler "wsm_GetAll"
End Function

The wsm_GetAll method of the SalesRankNPrice proxy class is called with the String value ISBN. The sc_SalesRankNPrice object then calls the GetAll Web method of the XML Web service. The result from the XML Web service is then returned to the calling method as a struct_All structure as defined in the class file.

The All User-Defined Complex Data Type

The Web Service References Tool 2.0 also creates a complex data type object factory class, the clsof_Factory_SalesRankNPrice class for the SalesRankNPrice XML Web service, which maps classes containing user-defined types on the client to a corresponding COM object on the Web server by using a type mapper. User-defined types are returned from the XML Web service as XML documents using the IXMLDOMNode interface. The clsof_Factory_SalesRankNPrice class contains the following code:

'*****************************************************************
'This class was created by the Web Service References Tool 2.0.
'
'Created: 4/30/2002 09:08:09 AM
'
'Description:
'The Generic Type Mapper is a SOAP Toolkit custom type mapper
  implementation
'that can map (serialize to XML and deserialize from XML) most of a
  user's COM
'objects. For more information, see Object Factory in Web Service
  References
'Tool 2.0 Help.
'
'Changes to the code in this class may result in incorrect behavior.
'
'*****************************************************************

Implements IGCTMObjectFactory

Private Function IGCTMObjectFactory_CreateObject(ByVal par_WSMLNode As
  MSXML2.IXMLDOMNode) As Object
    Dim node As IXMLDOMNode

    On Error GoTo IGCTMObjectFactoryTrap

    Set node = par_WSMLNode.Attributes.getNamedItem("targetClassName")

    Set IGCTMObjectFactory_CreateObject = Nothing

    If Not (node Is Nothing) Then
        Select Case node.nodeValue
            Case "struct_All"
                Set IGCTMObjectFactory_CreateObject = New struct_All
            Case "struct_Prices"
                Set IGCTMObjectFactory_CreateObject = New struct_Prices
            Case "struct_SalesRankNPrice1"
                Set IGCTMObjectFactory_CreateObject = New
                  struct_SalesRankNPrice1
            Case "struct_SalesRanks"
                Set IGCTMObjectFactory_CreateObject = New
                  struct_SalesRanks
        End Select
    End If

Exit Function

IGCTMObjectFactoryTrap:
    Err.Raise Err.Number, "clsof_Factory_SalesRankNPri",
      Err.Description
End Function

In addition to containing code to map the user-defined type in the struct_All class, there is also code for other user-defined types available for the SalesRankNPrice XML Web service.

The struct_All class file includes the following code, which contains the structure of the user-defined type:

...
'*****************************************************************
'This class was created by the Web Service References Tool 2.0.
'
'Created: 4/30/2002 09:08:09 AM
'
'Description:
'This class is a Visual Basic for Applications class representation of
  the user-defined
'type as defined by
  http://www.perfectxml.net/WebServices/
  SalesRankNPrice/BookService.asmx?wsdl.
'
'This class only contains the All,
'as defined in the WSDL.
'
'Changes to the code in this class may result in incorrect behavior.
'
'*****************************************************************

Public AmazonSalesRank As String
Public AmazonPrice As String
Public BNSalesRank As String
Public BNPrice As String
...

As you can see, this user-defined type consists of four String values used to contain the Amazon and Barnes & Noble sales rank and price for a book.

Calling the SalesRankNPrice XML Web Service

Now, all that's left is to create a subroutine to test the GetAll Web method. Since the SOAP 3.0 DLL provides the SoapClient30 object and the Web Service References Tool 2.0 handles all of the details of instantiating the object, and sending and receiving the SOAP messages containing the data, there's not much we need to do to use the SalesRankNPrice XML Web service. Look at the following code:

Private Sub TestGetAllMethod()
    Dim ComplexTypes As New clsws_SalesRankNPrice
    Dim TestOutput As New struct_All
    Dim strISBN As String

    strISBN = "0735612420"

    ' Call the XML Web service.
    Set TestOutput = ComplexTypes.wsm_GetAll(strISBN)

    MsgBox "Amazon ranks the book at " & TestOutput.AmazonSalesRank & _
        " and charges " & TestOutput.AmazonPrice & ". " & _
        "Barnes & Noble ranks the book at " & TestOutput.BNSalesRank &
          _
        " and charges " & TestOutput.BNPrice & "."
End Sub

First, we declare an object representing the SoapClient30 object and an object representing the struct_All user-defined type to hold the results from the XML Web service. Then, after setting strISBN String variable to an ISBN, we call the wsm_GetAll proxy method and assign the results. Next, we display the results. You should get the results shown in Figure 2.

Aa140321.odc_cmplxtypes02(en-us,office.10).gif

Figure 2. The results of the GetAll method

Now that we've seen how easy it is to use an XML Web service containing a user-defined type with the Web Service References Tool 2.0, let's briefly look at using a simple data type.

Using Simple Data Types

For our simple data type demonstration, we'll use the GetBNPrice Web method of the SaleRankNPrice XML Web service. This method accepts an ISBN String value and returns the BarnesAndNoble.com Price (in US Dollars) for that book as a String value.

The All complex data type is defined in the WSDL file with the following code:

...
<s:element name="GetBNPrice">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="ISBN"
              type="s:string" />
        </s:sequence>
    </s:complexType>
</s:element>
<s:element name="GetBNPriceResponse">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1"
              name="GetBNPriceResult" type="s:string" />
        </s:sequence>
    </s:complexType>
</s:element>
...

The Web Service References Tool 2.0 creates the following proxy function to call this method:

Public Function wsm_GetBNPrice(ByVal str_ISBN As String) As String
    '*****************************************************************
    'Proxy function created from 
      http://www.perfectxml.net/WebServices/
      SalesRankNPrice/BookService.asmx?wsdl.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetBNPriceTrap

    wsm_GetBNPrice = sc_SalesRankNPrice.GetBNPrice(str_ISBN)

Exit Function
wsm_GetBNPriceTrap:
    SalesRankNPriceErrorHandler "wsm_GetBNPrice"
End Function

This function calls the GetBNPrice Web method of the sc_SalesRankNPrice object with the str_ISBN parameter and returns the price as a String value. To test this method, you can use the following code:

Sub TestGetBNPriceMethod()
    Dim ComplexTypes As New clsws_SalesRankNPrice
    Dim strISBN As String
    Dim strPrice As String

    strISBN = "0735612420"

    strPrice = ComplexTypes.wsm_GetBNPrice(strISBN)

    MsgBox "The returned price is " & strPrice

End Sub

You get the results shown in Figure 3.

Aa140321.odc_cmplxtypes03(en-us,office.10).gif

Figure 3. The results of the GetBNPrice method

The procedure first declares a variable representing the clsws_SalesRankNPrice proxy class. A sample ISBN value is then assigned to the strISBN String variable. Next the wsm_GetBNPrice proxy method is called and the result from the XML Web service is assigned to the strPrice variable. And finally, a dialog box is displayed with the results.

After looking at this example, you can see that using XML Web services with simple data types is even easier than complex data types, as the Web Service References Tool 2.0 does most of the work for you.

Now we will examine an XML Web service that returns data as an XML node list.

Using XML as a Complex Data Type

In the following example, we will use the ZIP Code Resolver XML Web service. Given a valid street address, city, and state, this service returns the proper Zip Code, Zip Code+4, or USPS corrected address. This service exposes five methods including the CorrectedAddressXML Web method that accepts four String values arguments:

  • accessCode—Use "0" or "9999" for testing purposes.
  • address—A street address (for example, "One Microsoft Way").
  • city—A city name (for example, "Redmond").
  • state—A two-letter state postal abbreviation (for example, "WA").

The method returns the USPS address as a complex SOAP data type containing XML elements as follows:

<Street>1 MICROSOFT WAY</Street>
<City>REDMOND</City>
<State>WA</State>
<ShortZIP>98052</ShortZIP>
<FullZIP>98052-8300</FullZIP>

Using the code that the Web Service References Tool 2.0 generates, the following code builds a SOAP request, calls the CorrectedAddressXML Web method and uses the MSXML XML 4.0 parser to parse the SOAP response.

Note   The MSXML XML 4.0 parser is included with the Office XP Web Services Toolkit 2.0 and the SOAP Toolkit 3.0.

After setting a reference to the Zip Code Resolver XML Web service, the Web Service References Tool 2.0 creates the following files:

  • clsof_Factory_ZipCodeResolver Contains code that maps the structure in the struct_USPSAddress file with the methods in the COM object on the Web server.
  • struct_USPSAddress Contains the USPSADDRESS structure with street, city, state, shortzip, and fullzip elements.
  • clsws_ZipCodeResolver Contains code that encapsulates the Zip Code Resolver XML Web service methods.

Briefly looking at the code generated in the clws_ZipCodeResolver class module:

...
Private sc_ZipCodeResolver As SoapClient30
Private Const c_WSDL_URL As String =
  "http://webservices.eraserver.net/zipcoderesolver/
  zipcoderesolver.asmx?WSDL"
...

As with the previous example, two variables are declared representing the SoapClient30 proxy object and the URL to the XML Web service's WSDL file.

...
Private Const c_SERVICE As String = "ZipCodeResolver"
Private Const c_PORT As String = "ZipCodeResolverSoap"
Private Const c_SERVICE_NAMESPACE As String =
  "http://webservices.eraserver.net/"
...

Then, variables representing the name of the service, the port that receives the SOAP requests, and the XML Web service namespace, respectively, are declared.

Private Sub Class_Initialize()
    '*****************************************************************
    'This subroutine will be called each time the class is
     instantiated.
    'Creates sc_ComplexTypes as new SoapClient30, and then
    'initializes sc_ComplexTypes.mssoapinit2 with WSDL file found in
    'http://webservices.eraserver.net/
      zipcoderesolver/zipcoderesolver.asmx?WSDL.
    '*****************************************************************

    Dim str_WSML As String
    str_WSML = "<servicemapping name='ZipCodeResolver'>"
    str_WSML = str_WSML & "<service name='ZipCodeResolver'>"
    str_WSML = str_WSML & "<using 
      PROGID='MSSOAP.GenericCustomTypeMapper30' cachable='0'
      ID='GCTM'/>"
    str_WSML = str_WSML & "<types>"
    str_WSML = str_WSML & "<type name='USPSAddress' 
      targetNamespace='http://webservices.eraserver.net/' uses='GCTM'
        targetClassName='struct_USPSAddress'/>"
    str_WSML = str_WSML & "</types>"
    str_WSML = str_WSML & "</service>"
    str_WSML = str_WSML & "</servicemapping>"

    Set sc_ZipCodeResolver = New SoapClient30

    sc_ZipCodeResolver.MSSoapInit2 c_WSDL_URL, str_WSML, c_SERVICE,
      c_PORT, c_SERVICE_NAMESPACE
    'Use the proxy server defined in Internet Explorer's LAN settings
      by
    'setting ProxyServer to <CURRENT_USER>
    sc_ZipCodeResolver.ConnectorProperty("ProxyServer") =
      "<CURRENT_USER>"
    'Autodetect proxy settings if Internet Explorer is set to
      autodetect
    'by setting EnableAutoProxy to True
    sc_ZipCodeResolver.ConnectorProperty("EnableAutoProxy") = True

    Set sc_ZipCodeResolver.ClientProperty("GCTMObjectFactory") = New
      clsof_Factory_ZipCodeResolv

End Sub

Private Sub Class_Terminate()
    '*****************************************************************
    'This subroutine will be called each time the class is destructed.
    'Sets sc_ComplexTypes to Nothing.
    '*****************************************************************

    'Error Trap
    On Error GoTo Class_TerminateTrap

    Set sc_ZipCodeResolver = Nothing

Exit Sub

Class_TerminateTrap:
    ZipCodeResolverErrorHandler ("Class_Terminate")
End Sub

Private Sub ZipCodeResolverErrorHandler(str_Function As String)
    '*****************************************************************
    'This subroutine is the class error handler. It can be called from
      any class subroutine or function
    'when that subroutine or function encounters an error. Then, it
      will raise the error along with the
    'name of the calling subroutine or function.
    '*****************************************************************

    'SOAP Error
    If sc_ZipCodeResolver.faultcode <> "" Then
        Err.Raise vbObjectError, str_Function,
          sc_ZipCodeResolver.faultstring
    'Non SOAP Error
    Else
        Err.Raise Err.Number, str_Function, Err.Description
    End If

End Sub

The Class_Initialize event declares a variable for the concatenated String that maps the XML Web service's USPSAddress structure on the Web server to the proxy structure defined in the struct_USPSAddress class file. The procedure then instantiates the SoapClient30 object to pass XML Web service calls between the client and the Web server. The Class_Terminate event and ZipCodeResolverErrorHandler subroutine perform the same functions as explained in the previous example.

Public Function wsm_CorrectedAddressXml(ByVal str_accessCode As String,
  ByVal str_address As String, ByVal str_city As String, ByVal
  str_state As String) As struct_USPSAddress
    '*****************************************************************
    'Proxy function created from 
      http://webservices.eraserver.net/zipcoderesolver/
      zipcoderesolver.asmx?wsdl.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_CorrectedAddressXmlTrap

    Set wsm_CorrectedAddressXml =
      sc_ZipCodeResolver.CorrectedAddressXml(str_accessCode,
      str_address, str_city, str_state)

Exit Function
wsm_CorrectedAddressXmlTrap:
    ZipCodeResolverErrorHandler "wsm_CorrectedAddressXml"
End Function

The wsm_CorrectedAddressXml procedure accepts four String values representing an access code, street address, city and state. The procedure then calls the CorrectedAddressXml Web method of the sc_ZipCodeResolver object. The result is returned to the calling procedure as a struct_USPSAddress structure.

The struct_USPSAddress class file contains the following code:

...
'*****************************************************************
'This class was created by the Web Service References Tool 2.0.
'
'Created: 4/30/2002 10:43:50 AM
'
'Description:
'This class is a Visual Basic for Applications class representation of
  the user-defined
'type as defined by http://webservices.eraserver.net/
  zipcoderesolver/zipcoderesolver.asmx?WSDL.
'
'This class only contains the USPSAddress,
'as defined in the WSDL.
'
'Changes to the code in this class may result in incorrect behavior.
'
'*****************************************************************

Public Street As String
Public City As String
Public State As String
Public ShortZIP As String
Public FullZIP As String
...

The Web Service References Tool 2.0 also creates the object factory class representing the returned complex data type as follows:

...
'*****************************************************************
'This class was created by the Web Service References Tool 2.0.
'
'Created: 4/30/2002 10:43:50 AM
'
'Description:
'The Generic Type Mapper is a SOAP Toolkit custom type mapper
  implementation
'that can map (serialize to XML and deserialize from XML) most of a
  user's COM
'objects. For more information, see Object Factory in Web Service
  References
'Tool 2.0 Help.
'
'Changes to the code in this class may result in incorrect behavior.
'
'*****************************************************************

Implements IGCTMObjectFactory

Private Function IGCTMObjectFactory_CreateObject(ByVal par_WSMLNode As
  MSXML2.IXMLDOMNode) As Object
    Dim node As IXMLDOMNode

    On Error GoTo IGCTMObjectFactoryTrap

    Set node = par_WSMLNode.Attributes.getNamedItem("targetClassName")

    Set IGCTMObjectFactory_CreateObject = Nothing

    If Not (node Is Nothing) Then
        Select Case node.nodeValue
            Case "struct_USPSAddress"
                Set IGCTMObjectFactory_CreateObject = New
                  struct_USPSAddress
        End Select
    End If

Exit Function

IGCTMObjectFactoryTrap:
    Err.Raise Err.Number, "clsof_Factory_ZipCodeResolv",
      Err.Description
End Function
...

The following code can be used to test the CorrectedAddressXml Web method of the Zip Code Resolver XML Web service:

Public Sub GetAddressedAsXML()
    Dim objResolver As clsws_ZipCodeResolver
    Dim objNewAddr As struct_USPSAddress
    Dim str_accessCode As String
    Dim str_address As String
    Dim str_city As String
    Dim str_state As String
    Const TEST_ACCESS_CODE = "9999"

    ' Set the input strings.
    str_accessCode = TEST_ACCESS_CODE
    str_address = "One Microsoft Way"
    str_city = "Redmond"
    str_state = "WA"

    Set objResolver = New clsws_ZipCodeResolver
    Set objNewAddr = New struct_USPSAddress

    Set objNewAddr =
      objResolver.wsm_CorrectedAddressXml(str_accessCode, str_address,
      str_city, str_state)

    MsgBox "Street: " & objNewAddr.Street & vbCrLf & _
        " City: " & objNewAddr.City & vbCrLf & _
        " State: " & objNewAddr.State & vbCrLf & _
        " ShortZIP: " & objNewAddr.ShortZIP & vbCrLf & _
        " FullZIP: " & objNewAddr.FullZIP
End Sub

The details of this code are similar to the procedure we used to test the SaleRankNPrice XML Web service. The results are displayed as in Figure 4.

Aa140321.odc_cmplxtypes04(en-us,office.10).gif

Figure 4. The results of searching for ISBN 0735612420

We will now look at using other complex data types with the Web Service References Tool 2.0.

Using Arrays as Complex Data Types

Arrays are structures that can contain multiple values of the same data type at the same time. Arrays have a data type specification that is the same for all elements in the array. For example, in Visual Basic you can declare an array as a String type array, which means that all of the elements contained in the array will be of type String.

...
Dim arArray(4) As String
...

This statement declares an array called arArray with four String elements ranging from arArray(0) to arArray(3).

From the perspective of an XML Web service, an array contains elements representing multiple values so it is considered a complex data type.

The Web Service References Tool 2.0 makes using XML Web services that utilize arrays no more difficult than any of the other structure we have discussed so far. For example, let's assume that we have access to an ArraysSample XML Web service that contains the SortArray Web method, which accepts an array of names and returns the array with the elements sorted alphabetically in descending order. The procedure declaration for this method on the Web server could be as follows:

<WebMethod>
Private Function SortArray(ByVal arUnsortedArray As String) As String

After setting a reference to the WSDL file for the XML Web service, the Web Service References Tool 2.0 creates a corresponding method as follows:

Public Function wsm_SortArray(ByVal arUnsortedArray As Variant) As
  Variant
    '*****************************************************************
    'Proxy function created from http://SortArraysSample.asmx?wsdl.
    '
    '" arUnsortedArray " is an array with elements defined as String
    'See Complex Types: Arrays in Web Service References Tool 2.0 Help
    'for details on implementing arrays.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_ SortArray Trap

    wsm_SortArray = sc_ComplexTypes.SortArray(ar_SortArray)

Exit Function
wsm_SortArray Trap:
    ComplexTypesErrorHandler "wsm_SortArray "
End Function

Notice that the unsorted array and the return array are both declared as Variant data types. The Web Service References Tool 2.0 always defines arrays and array parameters as a Variant data type. When the data type of an incoming structure is different then the expected type, the receiving procedure will usually (but not always) try to coerce (change) the incoming data into the expected data type. In this case, the array contains elements with a String data type so successful coercion is likely. However, it is good programming practice to always try to match the input and output argument data types where possible. Unfortunately, the Web Service References Tool 2.0 can only use arrays declared as Variant data types.

The following code can be used to send an array to the ArraysSample XML Web service:

Sub sort_array()
    Dim objSort As New clsws_ComplexTypes
    Dim x(4) As String
    Dim z As Variant
    Dim idx As Integer

        x(0) = "Cat"
        x(1) = "Dog"
        x(2) = "Baseball"
        x(3) = "Apple"

    z = ExampleVar.wsm_SortArray(x)

    For idx = 0 To 3
        Debug.Print z(idx)
    Next idx
End Sub

After executing this code, you should expect the following output to be displayed in the Immediate window in the application hosting the Web Service References Tool 2.0:

Apple
Baseball
Cat
Dog

Now let's look at enumerated types as complex data types.

Using Enumerations as Complex Data Types

Often when developing applications, you'll find yourself needing to define a series of constants for some property or method. Enumerated types can provide enhanced usability in your code. For example, say you are developing a calendar application that uses the ordinal values for the days of the week. The following definition for an enumerated type would provide a user-friendly way of presenting these values:

...
Public Enum DaysOfWeek
    Sunday = 1
    Monday = 2
    Tuesday = 3
    Wednesday = 4
    Thursday = 5
    Friday = 6
    Saturday = 7
End Enum
...

To use the enumerated type DaysOfWeek, you could use the statement FirstDayofWeek = Sunday, which is much more usable than the statement

FirstDayofWeek = 1. As you might guess, enumerated types are also considered to be complex data types when using XML Web services.

The Web Service References Tool 2.0 defines enumerations sent to an XML Web service as a String type. Enumerations received from an XML Web service are defined as XML data and use the MSXML2.IXMLDOMSelection interface.

Sending an enumerated type to an XML Web service is straightforward:

Private Sub SendEnum()
    Dim SampleWebService As New clsws_SampleWebService
    Debug.Print SampleWebService.wsm_SendEnum("Sunday")
End Sub

In this procedure, we declare the SampleWebService variable as an instance of the XML Web service class. Then we pass the enumeration data to the XML Web service proxy method.

Returning an enumerated type requires a little more code:

Private Sub ReceiveEnum()
    Dim SampleWebService As New clsws_SampleWebService
    Dim x As MSXML2.IXMLDOMSelection

    Set x = SampleWebService.wsm_ReturnEnum
    Debug.Print x.Content.nodeTypedValue
End Sub

In this procedure, we declare the SampleWebService variable as an instance of the XML Web service class. Then, we declare a variable as MSXML2.IXMLDOMSelection. Next we set the MSXML2.IXMLDOMSelection variable to the returned value of the XML Web service and display the results.

Conclusion

In this article, we demonstrated using simple and complex data types in XML Web services by using the SOAP 3.0 DLL and the Web Service References Tool 2.0. Using the techniques and tools described in this article can increase the effectiveness of the applications you develop to use XML Web services.

Show: