While one return value for a method or function is a fairly normal approach in programming languages, SOAP is capable of returning values that are more complex. To demonstrate, the next example will test a service that returns information about a given US Zip Code, including the city, state, area code, and time zone.
Information about the service, including an HTML testing form that lets you see what results the service will produce, is available at http://webservicex.net/uszip.asmx?op=GetInfoByZIP. Its WSDL file is at http://webservicex.net/uszip.asmx?WSDL. If you test it with the Zip Code 13053, it will report back:
<?xml version="1.0" encoding="utf-8" ?> <NewDataSet> <Table> <CITY>Dryden</CITY> <STATE>NY</STATE> <ZIP>13053</ZIP> <AREA_CODE>607</AREA_CODE> <TIME_ZONE>E</TIME_ZONE> </Table> </NewDataSet>
The test reports back without a SOAP envelope. As the Web Services Toolkit will handle all the processing of the SOAP envelope and just hands your code the message inside, that won't be a problem for you. The Table here (and the <any /> in the schema in the WSDL file where these would appear) will lead the Web Services Toolkit to generate code that returns an IXMLDOMNodeList, as shown in Figure 9-9.
Figure 9-9. Generated code returning XML rather than a value
To work with this more complex data, the spreadsheet will have one source cell (for the Zip Code) and four result cells, as well as a button that will execute the web service call, as shown in Figure 9-10.
Figure 9-10. Spreadsheet base for running the web service
The code behind the "Get ZIP Info" button which is named ZipCoder is an extension of Example 9-1. It adds a few variables, and uses some XPath to extract the values of the XML elements returned by the SOAP call, as shown in Example 9-2.
Example 9-2. Calling a more complex web service
Private Sub ZipCoder_Click( ) Dim zipResolver As clsws_USZip Set zipResolver = New clsws_USZip Dim zip As String Dim city As String Dim state As String Dim areaCode As String Dim timeZone As String zip = Range("B1").Text Dim returnedNodes As MSXML2.IXMLDOMNodeList Set returnedNodes = zipResolver.wsm_GetInfoByZIP(zip) city = returnedNodes.Item(0).selectSingleNode("//CITY").Text state = returnedNodes.Item(0).selectSingleNode("//STATE").Text areaCode = returnedNodes.Item(0).selectSingleNode("//AREA_CODE").Text timeZone = returnedNodes.Item(0).selectSingleNode("//TIME_ZONE").Text Set cityRange = Range("B3") cityRange.Value = city Set stateRange = Range("B4") stateRange.Value = state Set areaCodeRange = Range("B5") areaCodeRange.Value = areaCode Set timeZoneRange = Range("B6") timeZoneRange.Value = timeZone End Sub
The main difference is in the way that information is returned from the web services call. Instead of the data coming back as a string, it comes back as a list of XML nodes, more precisely an MSXML2.IXMLDOMNodeList. To extract the individual values from the XML, the selectSingleNode method takes an XPath and returns the first node matching the XPath, here returning the CITY element, the text of which is then put into the city variable:
Dim returnedNodes As MSXML2.IXMLDOMNodeList Set returnedNodes = zipResolver.wsm_GetInfoByZIP(zip) city = returnedNodes.Item(0).selectSingleNode("//CITY").Text
Once the information is extracted, it's put into cells. If you enter a Zip Code into cell B1 and then click "Get ZIP Info," it puts the corresponding information into cells B3-B6, as shown in Figure 9-11.
Figure 9-11. Information about a Zip Code retrieved through a web service
You may encounter a problem with some services in which they return XML as a string, and the Toolkit returns that string rather than a searchable node list. To demonstrate, we'll connect to a different service that returns complex information, but reports it in the WSDL as a string. It's a stock quote application, which you can explore at http://www.webservicex.net/stockquote.asmx. The test page looks like Figure 9-12; note that the placeholder shown in the SOAP response's GetQuoteResult element near the bottom is a string.
Figure 9-12. Test page for a web service that returns XML content as text
A sample return value for the service looks like:
<?xml version="1.0" encoding="utf-8"?> <string xmlns="http://www.webserviceX.NET/"> <StockQuotes><Stock><Symbol>GLW</Symbol><Last>12 .90</Last><Date>2/20/2004</Date><Time>4:01pm</ Time><Change>-0.11</Change><Open>13.01</ Open><High>13.01</High><Low>12.66</ Low><Volume>15572300</Volume><MktCap>17.325B</ MktCap><PreviousClose>13.01</PreviousClose><PercentageChange>-0 .85%</PercentageChange><AnnRange>4.54 - 13.89</ AnnRange><Earns>-0.18</Earns><P-E>N/A</ P-E><Name>CORNING INC</Name></ Stock></StockQuotes></string>
There's XML in there, but for some reason the service's creator chose to present it as text. It should look like:
<?xml version="1.0" encoding="utf-8"?> <string xmlns="http://www.webserviceX.NET/"> <StockQuotes> <Stock> <Symbol>GLW</Symbol> <Last>12.90</Last> <Date>2/20/2004</Date> <Time>4:01pm</Time> <Change>-0.11</Change> <Open>13.01</Open> <High>13.01</High> <Low>12.66</Low> <Volume>15572300</Volume> <MktCap>17.325B</MktCap> <PreviousClose>13.01</PreviousClose> <PercentageChange>-0.85%</PercentageChange> <AnnRange>4.54 - 13.89</AnnRange> <Earns>-0.18</Earns> <P-E>N/A</P-E> <Name>CORNING INC</Name> </Stock> </StockQuotes> </string>
When the toolkit returns the string value, it will at least convert the < to < and > to >, making it easy to parse with a different part of the MSXML toolkit. Once again, build a spreadsheet to hold the information, with a button to call the service, as shown in Figure 9-13.
Figure 9-13. A spreadsheet for the stock quote service
Behind the button, the code shown in Example 9-3 will handle the conversion from text to XML and extract the contents of the XML to fields in the Excel spreadsheet.
Example 9-3. Processing XML returned by a web service as text
Private Sub GetQuote_Click( ) Dim symbol As String Dim stockObject As New clsws_StockQuote Dim xmlDoc As MSXML2.DOMDocument symbol = Range("B1").Text Set xmlDoc = New MSXML2.DOMDocument xmlDoc.LoadXml (stockObject.wsm_GetQuote(symbol)) Range("A6").Value = xmlDoc.selectSingleNode("//Last").Text Range("B6").Value = xmlDoc.selectSingleNode("//Date").Text Range("C6").Value = xmlDoc.selectSingleNode("//Time").Text Range("D6").Value = xmlDoc.selectSingleNode("//Change").Text Range("E6").Value = xmlDoc.selectSingleNode("//Open").Text Range("F6").Value = xmlDoc.selectSingleNode("//High").Text Range("G6").Value = xmlDoc.selectSingleNode("//Low").Text Range("H6").Value = xmlDoc.selectSingleNode("//Volume").Text End Sub
The highlighted portions show where MSXML2.DOMDocument, in particular its LoadXML method, is used in place of MSXML2.IXMLDOMNodeList. This accepts the string from the Toolkit, and parses it into XML, which can then be processed normally. The results look like Figure 9-14.
Figure 9-14. Results of checking a stock quote
If you ever have trouble with the values the toolkit hands you, take a close look at the generated code to see what type of data it is passing back to your application. Depending on how the service was initially structured, you may have to do some extra work.
There is no intrinsic limit on the amount of information a web service can return, and you may in fact want to present more complex information than these examples have shown. If the service returns a lot of data, XPath combined with the selectSingleNode and selectNodes methods will become a critical tool for picking out just the information you want.