Using Complex Data Types with XML Web Services in Microsoft Access 2002

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® Access 2002
    Microsoft Office Web Services Toolkit 2.0

Summary: Learn how XML Web services are an efficient way to expose the functionality of business and enterprise applications as a service, either over an intranet or the Internet. The Web Service References Tool 2.0 can make using XML Web services from a client application very simple. (40 printed pages)

Download setup.exe.

Contents

Introduction
The ComplexTypesDemo.mdb Access 2002 Database
The frmSimpleTypes Form
Combining Simple Data Types
The SalesRankNPrice XML Web Service
The All User-Defined Complex Data Type
The frmSalesRankNPrice Form
The frmZipCodeResolver Form
The frmCurrentWeather Form
Conclusion

Introduction

In this article, we will demonstrate using a sample Microsoft® Access 2002 database as a client application to access a number of XML Web services using both simple and complex data types. The database uses the Web Service References Tool 2.0, which is provided with the Microsoft Office XP Web Services Toolkit 2.0.

For more information on XML Web services and the Office XP Web Services Toolkit 2.0, see the article Using the Office XP Web Services Toolkit 2.0 to Interact with Complex Types in XML Web Services.

The ComplexTypesDemo.mdb Access 2002 Database

The ComplexTypesDemo.mdb is an Access 2002 database containing four forms, which are used to access and display the results of several XML Web services. The forms and the XML Web services they access are:

frmSimpleTypes This form demonstrates accessing XML Web services which use simple data types. This form uses the following XML Web services:

  • The WeatherRetriever XML Web service provides theGetTemperaturemethod which accepts a Zip Code as a String value and returns the current temperature as a Single value.
  • The TempConverter XML Web service provides theCtoFmethod which accepts a temperature in Fahrenheit as a Long value and returns the temperature in centigrade, also as a Long value, and theFtoCmethod which performs the inverse of theCtoFmethod.
  • The PigLatin XML Web service provides thetoPigLatinmethod which accepts an English phrase as a String value and returns the phrase in pig Latin, also as a String value.
  • The Romulan XML Web service provides theRomanToIntmethod which accepts an Integer value and returns the Roman numeral equivalent as a String value, and theIntToRomanmethod which performs the inverse of theRomanToIntmethod.
  • The NumToWords XML Web service provides theNumToWordsmethod which accepts a number as a Double value and returns a String value representing the number in words.
  • The Clock XML Web service provides the following:
    • TheGetDayOfYearmethod, which accepts a date as a String value and returns a value representing the current day of the year as a Double value.
    • TheGetDaysToEndYearmethod, which accepts a date as a String value and returns a value representing the number of days remaining in the current year as a Double value.
    • TheGetUTC_Timemethod, which returns the Universal Coordinated Time (UTC) for the current date as a Date value.
    • TheGetLocalTimemethod, which returns the local date and time for the current date as a Date value.
    • TheGetDayOfCurrentYearmethod, which returns the number of the day of the year for the current date as a Double value.
    • TheGetDaysToNextYearmethod, which returns the number of days remaining in this year for the current date as a Double value.

frmCurrentWeather This form accesses the WeatherRetriever XML Web service, which provides theGetWeathermethod. This method accepts a Zip Code as a String value and returns:

  • A complex data type structure (struct_CurrentWeather) consisting of four String values representing the date and time the weather data was last updated.
  • The URL to an icon representing the current weather conditions.
  • The current weather conditions.
  • The direction of the barometer reading.
  • Three Single values representing the current temperature, the current humidity, and the barometer reading.

frmSalesRankNPrice This form accesses the SalesRankNPrice XML Web service which provides a number of methods including theGetAllmethod. This method accepts an International Standard Book Number (ISBN) and returns a complex data type structure (struct_All) consisting of four String values representing the Amazon sales ranking, the Amazon price, the Barnes & Noble sales ranking, and the Barnes & Noble price.

frmZipCodeResolver This form accesses the ZipCodeResolver XML Web service, which provides theCorrectedAddressXmlmethod. This method accepts four String values representing an access code, a street address, city, and state, and returns a complex data type structure consisting of five String values representing a correct United States Postal Service (USPS) street address, city, state, short Zip Code, and full Zip Code (ZIP + 4).

The frmSimpleTypes Form

ThefrmSimpleTypesform demonstrates using the Web Service References Tool to access XML Web services using simple data types. The form consists of text boxes for input and output, and buttons for invoking the XML Web service (see Figure 1).

Aa140319.odc_accmplxtype01(en-us,office.10).gif

Figure 1. The frmSimpleTypes form

To use this XML Web service, the user types either a value in inches or a value in millimeters, and then clicks a button, which returns an equivalent value in inches or millimeters, depending on the type of input. For example, in Figure 1, I entered 144 in the inches box and the XML Web service returned 3657.6 millimeters.

When you set a reference to the WSDL file for this XML Web service, the Web Service References Tool creates theclsws_ConvertServiceclass, which contains the following code. 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:06:06 AM
'
'Description:
'This class is a Visual Basic for Applications class representation of 
  the Web service
'as defined by http://www.xmlbus.com:9010/xmlbus/container/
  Converter/ConverterService/ConverterPort?WSDL.
'
'To Use:
'Dimension a variable as new clsws_ConverterService, and then write 
  code to
'use the methods provided by the class.
'Example:
' Dim ExampleVar as New clsws_ConverterService
' debug.print ExampleVar.wsm_inchToMM("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 proxy object always starts with the prefix sc_ followed by the XML Web service name. The constantc_WSDL_URLwill always have the same name regardless of the class file.

...
' Dimensioning private class variables.
Private sc_ConverterService As SoapClient30
Private Const c_WSDL_URL As String = 
  "http://www.xmlbus.com:9010/xmlbus/container/
  Converter/ConverterService/ConverterPort?WSDL"
...

Then, other class variables are declared, representing a String value containing 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 = "ConverterService"
Private Const c_PORT As String = "ConverterPort"
Private Const c_SERVICE_NAMESPACE As String = "urn:target-converter-
  service"
...

Next, the Class_Initialize event first declares an empty String as str_MSML. As we will see later in this article, if the XML Web service being referenced uses a complex data type, thestr_WSMLvariable would contain a concatenated String value representing a mapping between the complex data type structure class created by the Web Service References Tool and the complex data type structure defined in the WSDL file.

Note   A Web Services Meta Language (WSML) file maps the methods of the XML Web service (as described in the WSDL file) to specific methods in the Component Object Model (COM) object which implements the XML Web service. The WSML file is only necessary for the implementation of the Microsoft SOAP dynamic-link library (DLL).

The procedure then uses the MSSoapInit2 method of the SoapClient30 proxy object to instantiate thesc_ConverterServiceobject. Next, various properties are set related to using Microsoft Internet Explorer with the proxy server:

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.xmlbus.com:9010/xmlbus/container/
      Converter/ConverterService/ConverterPort?WSDL.
    '*****************************************************************

    Dim str_WSML As String
    str_WSML = ""

    Set sc_ConverterService = New SoapClient30

    sc_ConverterService.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_ConverterService.ConnectorProperty("ProxyServer") = 
      "<CURRENT_USER>"
    'Autodetect proxy settings if Internet Explorer is set to 
      autodetect
    'by setting EnableAutoProxy to True
    sc_ConverterService.ConnectorProperty("EnableAutoProxy") = True
End Sub

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_ConverterService = Nothing

Exit Sub

Class_TerminateTrap:
    ConverterServiceErrorHandler ("Class_Terminate")
End Sub

TheConverterServiceErrorHandlersubroutine 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 ConverterServiceErrorHandler(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_ConverterService.faultcode <> "" Then
        Err.Raise vbObjectError, str_Function, 
          sc_ConverterService.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 two methods are generated.

Public Function wsm_inchToMM(ByVal sng_param0 As Single) As Single
    '*****************************************************************
    'Proxy function created from 
      http://www.xmlbus.com:9010/xmlbus/container/
        Converter/ConverterService/ConverterPort?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_inchToMMTrap

    wsm_inchToMM = sc_ConverterService.inchToMM(sng_param0)

Exit Function
wsm_inchToMMTrap:
    ConverterServiceErrorHandler "wsm_inchToMM"
End Function

Public Function wsm_mmToInch(ByVal sng_param0 As Single) As Single
    '*****************************************************************
    'Proxy function created from 
      http://www.xmlbus.com:9010/xmlbus/container/
        Converter/ConverterService/ConverterPort?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_mmToInchTrap

    wsm_mmToInch = sc_ConverterService.mmToInch(sng_param0)

Exit Function
wsm_mmToInchTrap:
    ConverterServiceErrorHandler "wsm_mmToInch"
End Function

Thewsm_inchToMMmethod is the proxy class for theinchToMMmethod of the XML Web service as representing by thesc_ConverterServiceobject. Thewsm_inchToMMmethod accepts a Single value representing a measure in inches and returns a Single value representing the equivalent measure in millimeters. Thewsm_mmToInchworks as the inverse of thewsm_inchToMMmethod, returning a value in inches for a measure in millimeters.

The following code is executed when the user clicks thecmdLengthcommand button.

Private Sub cmdLength_Click()
    Dim clsLength As New clsws_ConverterService
    Dim sglInch As Single
    Dim sglMM As Single
    
    On Error GoTo cmdLength_Click_Err
    
    DoCmd.Hourglass True
    Me![txtInch].SetFocus
    If Me![txtInch].Value <> "" Or Not IsNull(Me![txtInch].Value) Then
        sglInch = Trim(Me![txtInch].Value)
        sglMM = clsLength.wsm_inchToMM(sglInch)
        Me![txtMM].Value = sglMM
    ElseIf Me![txtMM].Value <> "" Or Not IsNull(Me![txtMM].Value) Then
        sglMM = Trim(Me![txtMM].Value)
        sglInch = clsLength.wsm_mmToInch(sglMM)
        Me![txtInch].Value = sglInch
    Else
        MsgBox "Please enter an inch or millimeter and click the 
          button."
        GoTo cmdLength_Click_End
    End If

cmdLength_Click_End:
    DoCmd.Hourglass False
    Exit Sub

cmdLength_Click_Err:
    MsgBox "The service is busy. Please try again later."
    GoTo cmdLength_Click_End
End Sub

First, we declare an object as clsws_ConverterService, which represents the SoapClient30 object used to provide access to the XML Web service methods. Then, after setting two String variables to hold the value entered by the user representing either inches or millimeters, we check to see whether the user typed a value in the inches box or the millimeters box. If inches were entered, we set thesglInchvariable to the value in the text box, and call thewsm_inchToMMproxy method and assign the results to the millimeter box for display. If the user entered a value in the millimeter box instead, we set thesglMMvariable to the value in the box, call the wsm_mmToInch proxy method, and display the results in the inches box. If the user didn't type a value in either box before clicking the button, we display a message and exit the procedure. You can see the results from this XML Web service in Figure 1.

Combining Simple Data Types

Now let's look at an example where we combine the simple data types returned from an XML Web service to present multiple, related data to the user. For this example, we will look at the Clock XML Web service, which provides a number of methods that send and receive simple data types.

This XML Web service provides theGetUTC_Timemethod which returns the Universal Coordinated Time (UTC) for the current date as a Date value, theGetLocalTimemethod which returns the local date and time for the current date as a Date value, theGetDayOfCurrentYearmethod which returns the number of the day of the year for the current date as a Double value, and theGetDaysToNextYearmethod which returns the number of days remaining in this year for the current date as a Double value. By combining the return results from these simple data types together into a single message, we can provide related data to the user and simulate a complex data type.

Looking at the code generated by the Web Service References Tool:

'Dimensioning private class variables.
Private sc_clock As SoapClient30
Private Const c_WSDL_URL As String = "http://www.xml-
  webservices.net/services/time_server/clock.asmx?WSDL"
Private Const c_SERVICE As String = "clock"
Private Const c_PORT As String = "clockSoap"
Private Const c_SERVICE_NAMESPACE As String = "http://www.xml-
  webservices.net/services/time_server"

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.xml-webservices.net/services/
      time_server/clock.asmx?WSDL.
    '*****************************************************************

    Dim str_WSML As String
    str_WSML = ""

    Set sc_clock = New SoapClient30

    sc_clock.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_clock.ConnectorProperty("ProxyServer") = "<CURRENT_USER>"
    'Autodetect proxy settings if Internet Explorer is set to 
      autodetect
    'by setting EnableAutoProxy to True
    sc_clock.ConnectorProperty("EnableAutoProxy") = True
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_clock = Nothing

Exit Sub

Class_TerminateTrap:
    clockErrorHandler ("Class_Terminate")
End Sub
Private Sub clockErrorHandler(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_clock.faultcode <> "" Then
        Err.Raise vbObjectError, str_Function, sc_clock.faultstring
    'Non SOAP Error
    Else
        Err.Raise Err.Number, str_Function, Err.Description
    End If
End Sub

The first sections of code are similar to those discussed in the previous example. After declaring variables representing attributes of the XML Web service such as service name, the URL to the WSDL file, and so forth, the Web Service References Tool generates subroutines to create or terminate the SoapClient30 object, and to provide error handling. Next, functions containing the proxy methods of the Clock XML Web service are generated:

Public Function wsm_GetUTC_Time() As Date
    '*****************************************************************
    'Proxy function created from http://www.xml-
      webservices.net/services/time_server/clock.asmx?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetUTC_TimeTrap

    wsm_GetUTC_Time = sc_clock.GetUTC_Time()

Exit Function
wsm_GetUTC_TimeTrap:
    clockErrorHandler "wsm_GetUTC_Time"
End Function
Public Function wsm_GetLocalTime() As Date
    '*****************************************************************
    'Proxy function created from http://www.xml-
      webservices.net/services/time_server/clock.asmx?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetLocalTimeTrap

    wsm_GetLocalTime = sc_clock.GetLocalTime()

Exit Function
wsm_GetLocalTimeTrap:
    clockErrorHandler "wsm_GetLocalTime"
End Function
Public Function wsm_GetDayOfCurrentYear() As Double
    '*****************************************************************
    'Proxy function created from http://www.xml-
      webservices.net/services/time_server/clock.asmx?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetDayOfCurrentYearTrap

    wsm_GetDayOfCurrentYear = sc_clock.GetDayOfCurrentYear()

Exit Function
wsm_GetDayOfCurrentYearTrap:
    clockErrorHandler "wsm_GetDayOfCurrentYear"
End Function
Public Function wsm_GetDaysToNextYear() As Double
    '*****************************************************************
    'Proxy function created from http://www.xml-
      webservices.net/services/time_server/clock.asmx?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetDaysToNextYearTrap

    wsm_GetDaysToNextYear = sc_clock.GetDaysToNextYear()

Exit Function
wsm_GetDaysToNextYearTrap:
    clockErrorHandler "wsm_GetDaysToNextYear"
End Function

As can be seen, each of these procedures are essentially identical in that they accept no input arguments (the current date is used by the XML Web service) and return a single data type. The first procedurewsm_GetUTC_Timegets the Coordinated Universal Time (UTC) of the current date.

The next procedures get the local date and time, the current day of the year, and the number of days left until the end of the year, respectively.

The following code is used to call each of these procedures and display the results in a message dialog box:

Private Sub cmdGetDateInfo_Click()
    Dim clsDateInfo As New clsws_clock
    Dim datUTC As Date
    Dim timLocal As Date
    Dim dblDayofYear As Double
    Dim dblDaysLeft As Double

    On Error GoTo cmdGetDateInfo_Click_Err

    DoCmd.Hourglass True

    datUTC = clsDateInfo.wsm_GetUTC_Time
    timLocal = clsDateInfo.wsm_GetLocalTime
    dblDayofYear = clsDateInfo.wsm_GetDayOfCurrentYear
    dblDaysLeft = clsDateInfo.wsm_GetDaysToNextYear

    MsgBox "The Coordinated Universal Time is " & datUTC & vbCrLf & _
        "The date and local time is " & timLocal & vbCrLf & _
        "Today is the " & dblDayofYear & " day of this year" & vbCrLf & 
          _
        "There are " & dblDaysLeft & " days left in this year.", 
          vbOKOnly, "Date Information"

cmdGetDateInfo_Click_End:
    DoCmd.Hourglass False
    Exit Sub

cmdGetDateInfo_Click_Err:
    MsgBox "The service is busy. Please try again later."
    GoTo cmdGetDateInfo_Click_End
End Sub

First, we declare an object asclsws_clockthat represents the SoapClient30 object. Then, we declare variables to hold the results of the method calls to the XML Web service. Next, we call each of the proxy methods and assign the results. And finally, we display a message dialog box and exit the procedure. You can see the results from this XML Web service in Figure 2.

Aa140319.odc_accmplxtype02(en-us,office.10).gif

Figure 2. Message displaying the results of the Clock XML Web service

ThefrmSimpleTypesform contains a number of additional classes generated by the Web Service References Tool as well as procedures that call other XML Web services using simple data types. Exploration of these class modules and associated subroutines is left to the reader.

Now we will look at XML Web services that use complex data types.

The SalesRankNPrice XML Web Service

ThefrmSalesRankNPriceform demonstrates accessing an XML Web service that uses complex data types by using the SalesRankNPrice XML Web service, which contains methods that support both simple and complex data types. The methods utilizing simple data types accept a String value and return a String value, as follows:

  • GetAmazonPrice (accepts a String value representing an ISBN; returns a String value representing the Amazon sales price).
  • GetAmazonSalesRank (accepts a String value representing an ISBN; returns a String value representing the Amazon sales rank).
  • GetAmazonUKPrice (accepts a String value representing an ISBN; returns a String value representing the Amazon UK sales price).
  • GetAmazonUKSalesRank (accepts a String value representing an ISBN; returns a String value representing the Amazon UK sales rank).
  • GetBNPrice (accepts a String value representing an ISBN; returns a String value representing the Barnes & Noble sales price).
  • GetBNSalesRank (accepts a String value representing an ISBN; returns a String value representing the Barnes & Noble sales rank).

In addition, the SalesRankNPrice XML Web service also supports complex data types:

  • GetAll (accepts a String value representing an ISBN; returns four String values representing the Amazon sales rank, the Amazon price, the Barnes & Noble sales rank, and the Barnes & Noble price).
  • GetAmazonAndBNPrice (accepts a String value representing an ISBN; returns two String values representing the Amazon price and the Barnes & Noble price).
  • GetAmazonAndBNSalesRank (accepts a String value representing an ISBN; returns two String values representing the Amazon sales rank and the Barnes & Noble sales rank).
  • GetAmazonSalesRankNPrice (accepts a String value representing an ISBN; returns two String values representing the Amazon sales rank and sales price).
  • GetBNSalesRankNPrice (accepts a String value representing an ISBN; returns two String values representing the Barnes & Noble sales rank and sales price).

To illustrate using complex data types with XML Web services, we will use the GetAll method in thefrmSalesRankNPriceform. This method accepts an ISBN as a String value and returns thestruct_Allstructure containing four String values.

Looking at the code generated by the Web Service References Tool, first, we declare variables representing the SoapClient30 object and the URL to the XML Web service's WSDL file, respectively.

...
'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"
WSML = c_WSML & " </servicemapping>"
...

The Class_Initialize event procedure first declares a concatenated String variable that maps the XML Web service'sAllcomplex data type (used with theGetAllmethod) 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

TheAllcomplex 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 thesc_SalesRankNPriceobject. TheClientPropertymethod of the SoapClient30 object is then used to create a new instance of theclsof_Factory_SalesRankNPriclass.

The Class_Terminate event is used to clean up resources that are no longer needed.

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

TheSalesRankNPriceErrorHandlersubroutine is used to handle any errors raised by the Web server hosting the XML Web service.

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 theGetAllWeb 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 is the generated procedure:

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

Thewsm_GetAllmethod is called with the String value ISBN. Thesc_SalesRankNPriceobject uses theGetAllmethod of the XML Web service. The result from the XML Web service is then returned to the calling method as astruct_Allstructure as defined in the class file of that same name.

The All User-Defined Complex Data Type

The Web Service References Tool creates a proxy structure in the client application that maps to the structure of the complex data type on the Web server. For the SalesRankNPrice XML Web service, this includes thestruct_Allclass that contains the following code:

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

The Web Service References Tool also creates theclsof_Factory_SalesRankNPriceclass, which maps classes containing user-defined types on the client to a corresponding COM object on the Web server by using a type mapper.

Note   More information about type mappers can be found in the Microsoft SOAP Toolkit 3.0.

User-defined types are returned from the XML Web service as XML data and are accessed by using the IXMLDOMNode interface. Theclsof_Factory_SalesRankNPriclass contains the following code:

...
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
...

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

The frmSalesRankNPrice Form

ThefrmSaleRankNPriceform utilizes the proxy methods described above to allow the user to type an ISBN and display the Amazon sales rank and price, and the Barnes & Noble sales rank and price. The following code is invoked from thecmdGetBookDatacommand button:

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

    On Error GoTo cmdGetBookData_Click_Err

    ' Indicate progress to the user.
    DoCmd.Hourglass True

    ' Clear the text boxes.
    txtAMSalesRank.SetFocus
    txtAMSalesRank = ""
    txtAMPrice.SetFocus
    txtAMPrice = ""
    txtBNSalesRank.SetFocus
    txtBNSalesRank = ""
    txtBNPrice.SetFocus
    txtBNPrice = ""

    ' Prompt for ISBN.
    txtISBN.SetFocus
    If IsNull(txtISBN) Or txtISBN = "" Then
        MsgBox "Please enter an ISBN and click the button."
        GoTo cmdGetBookData_Click_End
    Else
        strISBN = txtISBN

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

        ' Display the results.
        txtAMSalesRank = Trim(TestOutput.AmazonSalesRank)
        txtAMPrice = Trim(TestOutput.AmazonPrice)
        txtBNSalesRank = Trim(TestOutput.BNSalesRank)
        txtBNPrice = Trim(TestOutput.BNPrice)

        txtISBN.SetFocus
    End If

cmdGetBookData_Click_End:
    DoCmd.Hourglass False
    Exit Sub

cmdGetBookData_Click_Err:
    MsgBox "Error number: " & Err.Number & " " & Err.Description
    GoTo cmdGetBookData_Click_End
End Sub

First, we declare variables representing the SoapClient30 object, thestruct_Allclass, and a String variable to contain the ISBN. Next, we clear the text boxes on the form and then check to see make sure that the user entered an ISBN value. If the user didn't enter a value before clicking the button, we display a message and exit the procedure. Otherwise, we set thestrISBNvariable to the value in the text box, call thewsm_GetAllproxy method, and assign the results to theTestOutputvariable. Then, we parse the results, display the values, and exit the procedure. Notice that referencing the WSDL with the Web Service References Tool makes the objects and methods of the XML Web service available to the client application at design time. This gives you the added benefit of Microsoft IntelliSense® and syntax checking against XML Web services (also known as early binding). You can see the results from this XML Web service in Figure 3.

Aa140319.odc_accmplxtype03(en-us,office.10).gif

Figure 3. Results from the SalesRankNPrice XML Web service

The frmZipCodeResolver Form

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 theCorrectedAddressXMLmethod, which accepts four String values as 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 theCorrectedAddressXMLmethod, and uses the MSXML XML parser to parse the SOAP response.

Note   The MSXML XML 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 creates the following files:

  • clsof_Factory_ZipCodeResolv Contains code that maps the structure in thestruct_USPSAddressfile with the methods in the COM object on the Web server.
  • struct_USPSAddress Contains theUSPSADDRESSstructure withStreet, City, State, ShortZIP,andFullZIPvariables.
  • 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:

...
'Dimensioning private class variables.
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, 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 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
...

In the Class_Initialize event procedure, a concatenated String variable is declared that maps the XML Web service'sUSPSAddressstructure on the Web server to the proxy structure defined in thestruct_USPSAddressclass file. Then, the SoapClient30 proxy object is instantiated. The Class_Terminate event procedure and error handler serve the same purpose described previously.

...
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
...

Thewsm_CorrectedAddressXmlprocedure accepts four String values representing an Access code, street address, city and state. The procedure then calls theCorrectedAddressXmlmethod of thesc_ZipCodeResolverobject. The result is returned to the calling procedure as astruct_USPSAddressstructure.

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

Thestruct_USPSAddressclass file contains the following code:

...
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 also creates theclsof_Factory_ZipCodeResolvclass, which maps the returned complex data type to a generic type mapper as follows:

...
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
...

ThefrmZipCodeResolverform uses the following code to utilize theCorrectedAddressXmlmethod of the Zip Code Resolver XML Web service:

Private Sub cmdGetAddr_Click()
    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
    Dim loc As Integer
    Const TEST_ACCESS_CODE = "9999"

    On Error GoTo cmdGetAddr_Click_Err

    ' Indicate progress to the user.
    DoCmd.Hourglass True

    ' Set the input strings.
    str_accessCode = TEST_ACCESS_CODE
    str_address = txtInAddress
    str_city = txtInCity
    str_state = txtInState
    
    ' Clear the text boxes.
    txtOutStreet.SetFocus
    txtOutStreet = ""
    txtOutCity.SetFocus
    txtOutCity = ""
    txtOutState.SetFocus
    txtOutState = ""
    txtOutShortZip.SetFocus
    txtOutShortZip = ""
    txtOutFullZip.SetFocus
    txtOutFullZip = ""

    ' Create an instance of the XML Web service class.
    Set objResolver = New clsws_ZipCodeResolver
    ' Create an instance of the object to hold the return.
    Set objNewAddr = New struct_USPSAddress

    ' Call the proxy method.
    Set objNewAddr = 
      objResolver.wsm_CorrectedAddressXml(str_accessCode, _
        str_address, str_city, str_state)

    ' Display the return on the form.
    txtOutStreet = objNewAddr.Street
    ' For some reason, the return City field is prefixed by a
    ' carriage return so need to remove it.
    If InStr(1, objNewAddr.City, Chr(10)) Then
        txtOutCity = Right(objNewAddr.City, Len(objNewAddr.City) - _
            (InStr(1, objNewAddr.City, Chr(10)) + 2))
    Else
        txtOutCity = objNewAddr.City
    End If
    txtOutState = objNewAddr.State
    txtOutShortZip = objNewAddr.ShortZIP
    txtOutFullZip = objNewAddr.FullZIP

    txtInAddress.SetFocus
    
cmdGetAddr_Click_End:
    ' Reset the mouse cursor.
    DoCmd.Hourglass False
    Exit Sub

cmdGetAddr_Click_Err:
    MsgBox "Error number: " & Err.Number & " " & Err.Description
    GoTo cmdGetAddr_Click_End
End Sub

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

Aa140319.odc_accmplxtype04(en-us,office.10).gif

Figure 4. The results from the Zip Code Resolver XML Web service

The frmCurrentWeather Form

The last form we will briefly examine is thefrmCurrentWeatherform. This form accesses the WeatherRetriever XML Web service, which provides theGetWeathermethod. This method accepts a Zip Code as a String value and returns a complex data type structure (struct_CurrentWeather) consisting of four String values representing the date and time the weather data was last updated, a URL to an icon representing the current weather conditions, the current weather conditions, the direction of the barometer reading, and three Single values representing the current temperature, the current humidity, and barometer reading.

The Web Service References Tool generates the following code:

...
'Dimensioning private class variables.
Private sc_WeatherRetriever As SoapClient30
Private Const c_WSDL_URL As String = 
  "http://www.vbws.com/services/weatherretriever.asmx?WSDL"
Private Const c_SERVICE As String = "WeatherRetriever"
Private Const c_PORT As String = "WeatherRetrieverSoap"
Private Const c_SERVICE_NAMESPACE As String = "http://tempuri.org/"

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.vbws.com/services/weatherretriever.asmx?WSDL.
    '*****************************************************************

    Dim str_WSML As String
    str_WSML = "<servicemapping name='WeatherRetriever'>"
    str_WSML = str_WSML & "<service name='WeatherRetriever'>"
    str_WSML = str_WSML & "<using 
      PROGID='MSSOAP.GenericCustomTypeMapper30' cachable='0'
      ID='GCTM'/>"
    str_WSML = str_WSML & "<types>"
    str_WSML = str_WSML & "<type name='CurrentWeather' 
      targetNamespace='http://tempuri.org/' uses='GCTM'
      targetClassName='struct_CurrentWeather'/>"
    str_WSML = str_WSML & "</types>"
    str_WSML = str_WSML & "</service>"
    str_WSML = str_WSML & "</servicemapping>"

    Set sc_WeatherRetriever = New SoapClient30

    sc_WeatherRetriever.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_WeatherRetriever.ConnectorProperty("ProxyServer") = 
      "<CURRENT_USER>"
    'Autodetect proxy settings if Internet Explorer is set to 
      autodetect
    'by setting EnableAutoProxy to True
    sc_WeatherRetriever.ConnectorProperty("EnableAutoProxy") = True

    Set sc_WeatherRetriever.ClientProperty("GCTMObjectFactory") = New 
      clsof_Factory_WeatherRetrie

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_WeatherRetriever = Nothing

Exit Sub

Class_TerminateTrap:
    WeatherRetrieverErrorHandler ("Class_Terminate")
End Sub
Private Sub WeatherRetrieverErrorHandler(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_WeatherRetriever.faultcode <> "" Then
        Err.Raise vbObjectError, str_Function, 
          sc_WeatherRetriever.faultstring
    'Non SOAP Error
    Else
        Err.Raise Err.Number, str_Function, Err.Description
    End If

End Sub
...

These declarations and procedures are used the same way as discussed in the previous examples. TheGetWeathermethod of the WeatherRetriever XML Web service is included below:

Public Function wsm_GetWeather(ByVal str_zipCode As String) As
  struct_CurrentWeather
    '*****************************************************************
    'Proxy function created from 
      http://www.vbws.com/services/weatherretriever.asmx?WSDL.
    '*****************************************************************

    'Error Trap
    On Error GoTo wsm_GetWeatherTrap

    Set wsm_GetWeather = sc_WeatherRetriever.GetWeather(str_zipCode)

Exit Function
wsm_GetWeatherTrap:
    WeatherRetrieverErrorHandler "wsm_GetWeather"
End Function

ThefrmCurrentWeatherform consists of a text box where the user can type a Zip Code, a button that invokes the event procedure, and a number of other text boxes for displaying the results of the XML Web service. The form also includes an Image control that displays a GIF file indicating the current weather conditions. For example, if the XML Web service returns the weather condition as Sunny, then thesunny.giffile is displayed. In addition, there is a text box located under the Image control that contains theIconURLvalue returned from the XML Web service. This value is used in theGetImageprocedure to determine the image displayed. The OnCurrent event of thefrmCurrentWeatherform is used to set the Image control to a default GIF image as follows:

Private Sub Form_Current()
    On Error Resume Next
    Me![imgIcon].Picture = "C:\weather.gif"
End Sub

The following procedure is executed when the user clicks thecmdWeatherbutton:

Private Sub cmdWeather_Click()
    Dim clsWeather As clsws_WeatherRetriever
    Dim objCurrent As struct_CurrentWeather
    Dim strZIP As String
    Dim strURL As String
    Dim strImageName As String
    
    On Error GoTo cmdWeather_Click_Err
    
    DoCmd.Hourglass True
    
    Set clsWeather = New clsws_WeatherRetriever
    Set objCurrent = New struct_CurrentWeather
    
    Me![txtZIP].SetFocus
    If Me![txtZIP].Value = "" Or IsNull(Me![txtZIP].Value) Then
        MsgBox "Please enter a ZIP Code and click the button."
        GoTo cmdWeather_Click_End
    Else
        strZIP = Trim(Me![txtZIP].Value)
        Set objCurrent = clsWeather.wsm_GetWeather(strZIP)

        Me![txtTemp].SetFocus
        Me![txtTemp].Value = objCurrent.CurrentTemp

        Me![txtConditions].SetFocus
        Me![txtConditions].Value = objCurrent.Conditions

        Me![txtHumidity].SetFocus
        Me![txtHumidity].Value = objCurrent.Humidity

        Me![txtBarometer].SetFocus
        Me![txtBarometer].Value = objCurrent.Barometer

        Me![txtDirection].SetFocus
        Me![txtDirection].Value = objCurrent.BarometerDirection

        strURL = objCurrent.IconUrl

        strImageName = GetImage(strURL)

        Me![txtIcon].SetFocus
        Me![txtIcon].Value = strImageName

        Me![cmdWeather].SetFocus
    End If

cmdWeather_Click_End:
    DoCmd.Hourglass False
    Exit Sub

cmdWeather_Click_Err:
    MsgBox "The service is busy. Please try again later."
    GoTo cmdWeather_Click_End
End Sub

In addition to the tasks we have described in previous examples of determining whether the user entered a Zip Code before clicking the button, calling the proxy method of the XML Web service, and assigning the results to the various text boxes for display, this procedure also calls theGetImageprocedure. TheGetImageprocedure accepts the IconURLString value, which contains a URL containing a GIF file name that provides a visual indication of the current weather conditions. TheGetImageprocedure strips this file name from the IconURL argument, uses a Select Case . . . End Select statement to evaluate the extracted file name, and then returns a reference to a local GIF file, back to thecmdWeather_Clickprocedure. This process is useful because displaying a local file in an Image control is much easier and requires less code than using a URL.

Private Function GetImage(ByVal IconUrl As String) As String
    Dim strImageType As String

    strImageType = Right(IconUrl, Len(IconUrl) - (InStr(1, IconUrl, 
      "/images/") + 7))

    Select Case strImageType
        Case "partcloud.gif"
            GetImage = "C:\pcloudy.gif"
        Case "sunny.gif"
            GetImage = "C:\sunny.gif"
        Case "cloudy.gif"
            GetImage = "C:\cloudy.gif"
        Case "rainy.gif"
            GetImage = "C:\rain.gif"
        Case Else
            GetImage = "C:\weather.gif"
    End Select    
End Function

In order for the image to be updated each time the XML Web service returns a new result, we include the following code in the OnLostFocus event of thetxtIcontext box:

Private Sub txtIcon_LostFocus()
    On Error Resume Next
    Me![imgIcon].Picture = Me![txtIcon]
End Sub

The results of using this XML Web service are displayed in Figure 5.

Aa140319.odc_accmplxtype05(en-us,office.10).gif

Figure 5. The results of the WeatherRetriever XML Web service

Most of the XML Web services we have used in this article also include other methods that you should explore. In addition, the XML Web services described here represent just a very small number of the XML Web services that exist. That number is growing daily as more individuals and companies see the advantage to using XML Web services for extending their services.

Conclusion

In this article, we demonstrated using a sample Microsoft Access 2002 database to access a number of XML Web services using both simple and complex data types. The database used the Web Services References Tool 2.0, which is provided with the Office XP Web Services Toolkit 2.0. By exploring the code and techniques used in this article, you will be able to incorporate the power of XML Web services into your own applications.