set safety OFF clear *!* http://www.capescience.net/webservices/globalweather/index.shtml *!* Choose your own airport: see the link above for details. *#DEFINE AIRPORT_LOCATION "KORD" #DEFINE AIRPORT_LOCATION "FAY" SET ALTERNATE TO output.txt SET ALTERNATE ON LOCAL loConn as MSSoap.HttpConnector, ; loSerializer as MSSoap.SoapSerializer, ; loReader as MSSoap.SoapReader loConn = CREATEOBJECT("MSSoap.HttpConnector") loSerializer = CREATEOBJECT("MSSoap.SoapSerializer") loReader = CREATEOBJECT("MSSoap.SoapReader") *!* loConn is an HTTPConnector: it actually sends the messages through HTTP to the *!* web service which is specified as the EndPointURL. Many connector properties are *!* exposed through the Property() collection, rather than being addressed as *!* loConn.EndPointURL, for example. loConn.Property("EndPointURL") = "http://live.capescience.com/ccx/GlobalWeather" loConn.Connect() *!* BeginMessage tells the web service that we're going to start talking to it. *!* The Connector has an InputStream and an OutputStream -- we send data by *!* writing to the InputStream, and read it out from the OutputStream. loConn.BeginMessage() *!* The SOAPSerializer object builds a SOAP message while hiding us from the *!* nitty-gritty of the XML it's building. It builds the message, and sends *!* it out on the InputStream it's passed in the Init. loSerializer.Init(loConn.InputStream) *!* Here's where we start building the message. Inside the envelope, you can *!* specify other parameters, such as encoding: however, I found that this *!* service works fine with the defaults. loSerializer.startEnvelope() loSerializer.startBody *!* The method call and its parameters are all XML elements within the message, *!* so we just need to add them as such. First we add the name of the element. *!* Then we add the namespace within which it is found, and the encoding it uses. loSerializer.startElement("getWeatherReport", "capeconnect:GlobalWeather:GlobalWeather", "NONE") *!* The parameters are elements within the method element. loSerializer.startElement("code") *!* WriteString is how we send straight text to the serializer. loSerializer.writeString(AIRPORT_LOCATION) *!* Here we close the various elements we've opened. loSerializer.endElement() loSerializer.endElement() loSerializer.endBody() loSerializer.endEnvelope() loConn.endMessage() *!* We now load the outputStream from the HTTPConnector into a SOAPReader. *!* This changes the XML into an object that we can operate on. loReader.Load(loConn.OutputStream) *!* Write the XML out so we can look at it separately -- e.g., in IE. STRTOFILE(loReader.DOM.xml, "gw.xml") *!* To get down to the actual forecast, we have to open the envelope and the other *!* layers that wrap the returned data. loEnvelope = loReader.DOM.childNodes(1) loBody = loEnvelope.childNodes(1) loResponse = loBody.childNodes(1) loForecast = loResponse.childNodes(1) *!* Now, we walk down the tree recursively, formatting the XML as we go along. I'm *!* removing the XML tags and indenting the text with tabs to match the layers *!* within the file. *!* *!* There is one known bug in this code: when it gets down to the Sky conditions, *!* if there's only one layer, it will output the layer as a text string instead *!* of parsing it out. Feel free to fix this. :-) lcTab = "" ? ParseNode(loForecast, lcTab) SET ALTERNATE OFF SET ALTERNATE TO MODIFY FILE output.txt FUNCTION ParseNode LPARAMETERS toNode, tcTab LOCAL lcRetVal as string, loChild as MSXML2.IXMLDOMNode lcRetVal = "" IF toNode.childNodes.Length <= 1 *!* If there's nothing else to recurse, output the information from this node. lcRetVal = toNode.Text ELSE *!* Otherwise, scan through the child nodes and parse each one of them. FOR EACH loChild IN toNode.ChildNodes *!* If you don't check this, you get an extra blank entry for the *!* container before you recurse into it. IF NOT EMPTY(loChild.baseName) lcRetVal = "" + lcRetVal + ; CHR(13) + CHR(10) + ; tcTab + loChild.baseName + " -- " + ParseNode(loChild, tcTab + CHR(9)) ENDIF ENDFOR ENDIF RETURN lcRetVal