I’ve been working on some demos with the SOAP Toolkit and one of the things that does not appear to be easily accomplished is passing complex data to a Web Service from a Visual FoxPro (or other COM client application). If you call a Web Service that expects a complex type on the Web Service side – for example a DataSet or a simple Business Entity object – you can’t just simply pass an object reference and have the MSSOAP client serialize it.
In order to pass an object you either have to create the SOAP response manually using the low level SOAP API (fair amount of code), create a special serializer yourself (complex process) or pass the XML content for the object in the form of a NodeList.
The latter of these I wasn’t aware of until today. In fact, in the past I had pretty much resigned myself to creating server side Web Services that accept string parameters that allow XML to be passed in and be parsed into the proper objects on the server side.
This works OK if you are in control of the server side, but there’s a fair amount of overhead in the double encoding that occurs of the XML string (once encoding into XML, then encoding the XML into an XML Safe string).
It turns out that you CAN pass XML content directly to the SOAP Parser as a parameter by creating an XMLNodeList of the XML content that is to be passed. This still isn’t exactly transparent, but at least this mechanism allows for calling a SOAP Web Service where a complex parameter is required assuming we can assemble the proper XML.
The most common scenario I’ve been running into is passing a DataSet to a Web Service. The following example demonstrates posting changes made to a VFP Cursor (Authors from the Pubs Sql Server Database) and saving them back to the server by passing a DataSet to the UpdateAuthorData() method on the server.
oSOAP = CREATEOBJECT("MSSOAP.SoapClient30")
oSOAP.MSSoapInit(lcWSDLUrl)
SELE AUTHORS && Assume Authors is loaded and changes where made
*** Generate the DiffGram DataSet XML
LOCAL oXA as XMLAdapter
oXA = CREATEOBJECT("XMLAdapter")
oXA.UTF8Encoded = .t.
oXA.AddTableSchema("Authors")
oXA.IsDiffgram = .T.
lcXML = ""
oXA.ToXML("lcXML",,.f.,.T.,.T.)
*** Convert the XML into a NodeList
loDOM = CREATEOBJECT("MSXML2.DomDocument")
loDom.LoadXMl(lcXML)
loNodeList = loDom.DocumentElement.ChildNodes()
*** Retrieve authors - returns DataSet returned as NodeList
LOCAL loException as Exception
loException = null
TRY
*** Pass the NodeList as the DataSet parameter
llResult = oSOAP.UpdateAuthorData(loNodeList)
CATCH TO loException
llError = .t.
ENDTRY
*** Always check for errors
IF !ISNULL(loException)
lcError = oSOAP.FaultString
if EMPTY(lcError)
lcError = loException.Message
ENDIF
? lcError
? loException.ErrorNo
RETURN
ENDIF
*** Print the reslt - .t. or .f.
? llResult
RETURN
The key features here are taking the XML returned from the XMLAdapter and creating a NodeList from it and then passing that Nodelist to the Web Service function UpdateAuthorData() which expects a DataSet as its input value.
This code:
loDOM = CREATEOBJECT("MSXML2.DomDocument")
loDom.LoadXMl(lcXML)
loNodeList = loDom.DocumentElement.ChildNodes()
Loads the XMLAdapter generated DiffGram XML into a DOM object and then retrieves the ChildNodes collection. The Soap client provides the root node for the parameter, and the detail is parsed into it from the passed NodeList which gets embedded into the SOAP Document as raw XML. Note that you have to make sure you generate your XML with the proper Encoding – UTF8 typically.
The same mechanism can be applied for objects passed to the Web Service from the client. All complex types are passed as embedded XML fragments and this mechanism allows you to directly embed this data.
This approach still doesn’t solve the problem of parsing objects into XML for transfer in the first place, but at least it gives us the ability to generate the appropriate XML as needed and at least be able to call these methods if string methods aren’t available. For DataSets this process is nice because VFP can generate the appropriate XML for us directly. With other objects you probably will have to manually create the XML or use a tool like wwXML to help with serialization.
Other Posts you might also like