I know the subject of consuming web services has been covered elsewhere in posts and documents. However, all of them seem to either use a simple string for the XML message or build it element by element. The problem is that the former is no good for large messages and the latter is a bit unwieldy, especially when we have XMLports available.
So here is a post showing how to use XMLports to handle the XML messages when you want to consume a webservice. The scenario is that we want to send a request for data to a webservice and get back one or more records. We will use HTTP POST to communicate with the webservice.
Start with the format of the request message:
(By the way, the website and webservice do NOT exist so do NOT bother trying to use them)
POST /DataService.asmx HTTP/1.1
Host: fat-aardvark.net
Content-Type: text/xml; charset=utf-8
SOAPAction: “http://tempuri.org/SelectData”
<soap:Envelope xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema”>
soap:Body
</soap:Body>
</soap:Envelope>
To which the webservice responds with this:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<soap:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/”>
soap:Body
int
int
int
string
int
int
int
string
</soap:Body>
</soap:Envelope>
Note that the data element will be repeated once per record that is being returned.
We’ll start by building an XMLport to create the request message.
TagName TagType SourceType DataSource
soap:Envelope Element Text soap:Envelope
xmlns:soap Attribute Text NS1
xmlns:xsi Attribute Text NS2
xmlns:xsd Attribute Text NS3
soap:Body Element Text soap:Body
SelectData Element Text
xmlns Attribute Text NS4
Add the following text constants to hold all the namespaces in the request
Name ConstValue
NSpace1 http://schemas.xmlsoap.org/soap/envelope/
NSpace2 http://www.w3.org/2001/XMLSchema-instance
NSpace3 http://www.w3.org/2001/XMLSchema
NSpace4 http://tempuri.org/
And put them in the right values when we run the XMLport with this bit of code
OnPreXMLport()
NS1 := NSpace1;
NS2 := NSpace2;
NS3 := NSpace3;
NS4 := NSpace4;
Obviously, if you need to send some other data then you put the relevant code/elements/attributes into this XMLport.
Now we need an XMLport to handle the response. I know later XMLports allow you to read in XML namespace information but this example will strip that out of the response so that we don’t have to worry about it. This will allow you to use this example in earlier systems too.
TagName TagType SourceType DataSource
Envelope Element Text
Body Element Text
SelectDataResponse Element Text
SelectDataResult Element Text
Data Element Text
ID Element Text D1
Code Element Text D2
Type Element Text D3
Name Element Text D4
When this runs, it will read each record into the D1, D2, D3 & D4 global variables and you will then need to add code to the XMLport to do something with that data (remember that they will be text values). Alternatively, you could specify a table and replace the D1/2/3/4 with field names in that table. It’s up to you what you do with the XMLport once it has the data.
Now for a codeunit to bring them together and do the dirty work.
Setup some global variables:
Name DataType Subtype
XP1 XMLport
XP2 XMLport
XMLDoc Automation ‘Microsoft XML, v5.0’.DOMDocument
XMLHttpConn Automation ‘Microsoft XML, v5.0’.ServerXMLHTTP
InStr InStream
OutStr OutStream
Add a function called SendMessage and setup a local variable:
Name DataType Subtype
TempTable Record TempBlob (set this as a temporary table!)
Add another function called RemoveNamespace which has the parameters:
Var Name DataType Subtype
No Source Automation ‘Microsoft XML, v5.0’.DOMDocument
Yes Destination Automation ‘Microsoft XML, v5.0’.DOMDocument
and the local variables:
Name DataType Subtype
TempTable Record TempBlob (set this as a temporary table!)
XMLStyleSheet Automation ‘Microsoft XML, v5.0’.DOMDocument
StyleOutStr OutStream
StyleInStr InStream
Now add the code as follows:
======================================================
OnRun()
SendMessage;
======================================================
SendMessage()
//setup the temporary table so that we can handle the XML without saving it to disk first
//create a couple of streams to transfer the data in and out of the BLOB field
CLEAR(TempTable);
TempTable.Blob.CREATEINSTREAM(InStr);
TempTable.Blob.CREATEOUTSTREAM(OutStr);
//the request XMLport fills the BLOB with the XML message
CLEAR(XP1);
XP1.SETDESTINATION(OutStr);
XP1.EXPORT;
//load the message into the XML automation variable
IF ISCLEAR(XMLDoc) THEN
CREATE(XMLDoc);
XMLDoc.load(InStr);
//this is for diagnostics only, so you can see what the XMLport actually produced
XMLDoc.save(‘C:\Temp\XMLRequest.txt’);
//create the HTTP connector
IF ISCLEAR(XMLHttpConn) THEN
CREATE(XMLHttpConn);
//tell it where the web service is located
XMLHttpConn.open(‘POST’,‘http://fat-aardvark.net/DataService.asmx’,FALSE);
//set some values in the request header depending on what the service requires
XMLHttpConn.setRequestHeader(‘Host’,‘fat-aardvark.net’);
XMLHttpConn.setRequestHeader(‘SOAPAction’,‘http://tempuri.org/SelectData’);
XMLHttpConn.setRequestHeader(‘Content-Type’,‘text/xml’);
//actually send the message
XMLHttpConn.send(XMLDoc);
//get the response
XMLDoc.load(XMLHttpConn.responseXML);
//tell us if we got an error (it is 200 because the response definition said “200 OK”)
IF XMLHttpConn.status <> 200 THEN BEGIN
MESSAGE(‘Status %1 %2’,XMLHttpConn.status,XMLHttpConn.statusText);
EXIT;
END;
//this is for diagnostics only, so you can see what you got back
XMLDoc.save(‘C:\Temp\XMLResponse1.xml’);
//take away the namespaces
RemoveNamespace(XMLDoc,XMLDoc);
//this is for diagnostics only, so you can see what it looks like after the namespaces have gone
XMLDoc.save(‘C:\Temp\XMLResponse2.xml’);
//fill the BLOB with the response XML
XMLDoc.save(OutStr);
//the response XMLport reads the data from the BLOB and processes it
CLEAR(XP2);
XP2.SETSOURCE(InStr);
XP2.IMPORT;
======================================================
RemoveNamespace
//this has been taken from a Microsoft knowledgebase aricle and strips out the
//namespaces from an XML message using a style sheet
TempTable.Blob.CREATEOUTSTREAM(StyleOutStr);
TempTable.Blob.CREATEINSTREAM(StyleInStr);
StyleOutStr.WRITETEXT(’<?xml version="1.0" encoding="UTF-8"?>’);
StyleOutStr.WRITETEXT(’<xsl:stylesheet version=“1.0” xmlns:xsl=“http://www.w3.org/1999/XSL/Transform">’);
StyleOutStr.WRITETEXT(’<xsl:output method=“xml” encoding=“UTF-8” />’);
StyleOutStr.WRITETEXT(’<xsl:template match=”/">’);
StyleOutStr.WRITETEXT(‘xsl:copy’);
StyleOutStr.WRITETEXT(’<xsl:apply-templates />’);
StyleOutStr.WRITETEXT(’</xsl:copy>’);
StyleOutStr.WRITETEXT(’</xsl:template>’);
StyleOutStr.WRITETEXT(’<xsl:template match="">’);
StyleOutStr.WRITETEXT(’<xsl:element name="{local-name()}">’);
StyleOutStr.WRITETEXT(’<xsl:apply-templates select="@ | node()" />’);
StyleOutStr.WRITETEXT(’</xsl:element>’);
StyleOutStr.WRITETEXT(’</xsl:template>’);
StyleOutStr.WRITETEXT(’<xsl:template match="@*">’);
StyleOutStr.WRITETEXT(’<xsl:attribute name="{local-name()}"><xsl:value-of select="."/></xsl:attribute>’);
StyleOutStr.WRITETEXT(’</xsl:template>’);
StyleOutStr.WRITETEXT(’<xsl:template match=“text() | processing-instruction() | comment()”>’);
StyleOutStr.WRITETEXT(’<xsl:copy />’);
StyleOutStr.WRITETEXT(’</xsl:template>’);
StyleOutStr.WRITETEXT(’</xsl:stylesheet>’);
IF ISCLEAR(XMLStyleSheet) THEN
CREATE(XMLStyleSheet);
XMLStyleSheet.load(StyleInStr);
IF ISCLEAR(Destination) THEN
CREATE(Destination);
Source.transformNodeToObject(XMLStyleSheet,Destination);
There you go. Hope it helps someone out there.
No warranty express or implied.
Cheers
John
P.S. Sorry about the formatting, it looks like it’s got a bit mangled when I pasted it in here.