XML

SOAP Application Using XML DOM

The SOAP application you are about to create will use Visual Basic on the client side and Active Server Pages (ASP) on the server side. The XML DOM will be used on both the client and the server.

Instead of loading XML data from a file or a string, this application will build an XML file using the XML DOM objects and show you how to write code to build XML documents dynamically.

In a real application, you could build the XML document using a Visual Basic application that processes SOAP requests or use JScript or VBScript in a browser application with the values that the user has inputted into the browser form. For this example, we will provide values for the XML elements.

We will start by creating a new Visual Basic EXE project. For this example, you will need a copy of the XML parser, which at the time of this printing is version 3. Thus, you should add a reference to the Microsoft XML 3.0 by choosing References from the Project menu, and then choosing Microsoft XML version 3 from the References dialog box.

Add a button to the frmDOMTest form with the name cmdSoap and the caption Soap. Enter the following code into the click event handler of this button:

  Dim objXMLDoc As DOMDocument30
  Dim objHTTPRequest As XMLHTTP30
  Dim objXMLDocResponse As DOMDocument30
  Set objHTTPRequest = New XMLHTTP30
  Set objXMLDoc = New DOMDocument
  Set objXMLDocResponse = New DOMDocument30
  Set objXMLDocNode = New DOMDocument30
  Dim lngResponse As Long
  Dim lngOrderID As Long
  Dim strResponse As String
  objXMLDoc.async = False
  'We will begin by using the loadXML string to load in the root
  'node.
  objXMLDoc.loadXML _
      "<SOAP-ENV:Envelope xmlns:SOAP-ENV = " & _
      "'http://schemas.xmlsoap.org/soap/envelope'" & _
      " xmlns:xsi='http://www.w3.org/1999/XMLSchema/instance' " & _
      " xsi:schemaLocation=" & _
      "'http://www.northwindtraders.com/schemas/NPOSchema.xsd'>" & _
      "<SOAP-ENV:Body xsi:type='northwindBody'><UpdatePO>" & _
      "<OrderID>" & "</OrderID>" & _
      "<return>" & strReturn & _
      "</return></UpdatePO></SOAP-ENV:Body>" & _
      "</SOAP-ENV:Envelope>"
  'The createNode method returns a node object.
  'If the element has a namespace prefix, it must be included.
  'The appendChild method will take the node object created with
  'createNode and add it to XML document node collection.
  objXMLDoc.documentElement.appendChild _
      objXMLDoc.createNode(NODE_ELEMENT, "SOAP-ENV:Body", _
      "'http://schemas.xmlsoap.org/soap'")
  'For an attribute, you must use the attributes property.
  objXMLDoc.documentElement.firstChild.Attributes.setNamedItem _
      objXMLDoc.createNode(NODE_ATTRIBUTE, "xsi:type", _
      "xmlns:xsi='http://www.w3.org/1999/XMLSchema/instance'")
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body").appendChild _
      objXMLDoc.createNode(NODE_ELEMENT, "UpdatePO", "")
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
      appendChild objXMLDoc.createNode(NODE_ELEMENT, "OrderID", "")
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
      appendChild objXMLDoc.createNode _
      (NODE_ELEMENT, "CustomerNumber", "")
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
      appendChild objXMLDoc.createNode(NODE_ELEMENT, "Item", "")
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
      appendChild objXMLDoc.createNode(NODE_ELEMENT, "Quantity", "")
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
      appendChild objXMLDoc.createNode(NODE_ELEMENT, "return", "")
  'We must now set the values for each node.
  'We will use XSL in selectSingleNode to get the node.
  'For the attribute, we must use getNamedItem to get the attribute.
  objXMLDoc.selectSingleNode("SOAP-ENV:Envelope/SOAP-ENV:Body"). _
      Attributes.getNamedItem("xsi:type").nodeValue = "NorthwindBody"
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/OrderID").Text = "0"
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/CustomerNumber"). _
      Text = "999"
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Item").Text = "89"
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/return").Text = "0"
  objXMLDoc.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Quantity"). _
      Text = "35"
  'Initialize HTTP request with Post.
  objHTTPRequest.open "POST", "http://localhost/XMLSample/SOAP.asp"
  'Set the SOAP headers.
  objHTTPRequest.setRequestHeader "POST", "/Northwind.Order HTTP/1.1"
  objHTTPRequest.setRequestHeader "Host", "www.northwindtraders.com"
  objHTTPRequest.setRequestHeader "Content-Type", "text/xml"
  objHTTPRequest.setRequestHeader "content-length", _
      Len(objXMLDoc.xml)
  objHTTPRequest.setRequestHeader _
      "SOAPAction", "urn: northwindtraders.com:PO#UpdatePO"
  'Send the message.
  objHTTPRequest.send objXMLDoc.xml
  'Set the response document object equal to the responseXML object.
  Set objXMLDocResponse = objHTTPRequest.responseXML
  'Wait to get result.
  Dim lLoopNum As Long
  Do While objXMLDocResponse.selectSingleNode _
  ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/return") Is Nothing _
      And lLoopNum < 10000
      DoEvents
      lLoopNum = lLoopNum + 1
  Loop
  'Get the return values.
  strResponse = objXMLDocResponse.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/return").Text
  If strResponse = "" Then
      'Raise an error here.
  Else
      MsgBox "Response = " & strResponse
  End If
  lngOrderID = CLng(objXMLDocResponse.selectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/OrderID").Text)
  Set objXMLDocResponse = Nothing
  Set objXMLDoc = Nothing
  Set objHTTPRequest = Nothing

To be able to run the application, you need to create an object named objOrderPO that contains the UpdatePO method. Since we will not actually create this object, we will comment out the code where the method will be created and create dummy variables so you can run the example. You also need to create an ASP page named SOAP.asp and put it on the local server in a folder called XMLSample under the default Web site. Now let's take a look at how to create an ASP page.

The ASP page uses two document objects, one called objXMLDocRequest, which holds the XML document sent from the server, and the other called objXMLDocResponse, which contains the XML document that is returned to the server. The names of the object and the method that need to be called are retrieved from the header first. Next you retrieve the XML document from the request object, retrieve the parameter values located in the XML body, and finally create the object and call the method. Once the method has been called, the return XML string is built and placed in the response object.

Create an ASP page and add the following code:

  <%@ Language=VBScript %>
  <SCRIPT LANGUAGE=vbscript RUNAT=Server>
  Dim objXMLDocRequest
  Dim objXMLDocResponse
  Dim result, strObject, strMethod
  Dim strCustomerNumber, strItem, strReturn
  Dim strOrderID, strQuantity
  Set objXMLDocRequest= Server.CreateObject ("Microsoft.XMLDOM")
  Set objXMLDocResponse= Server.CreateObject ("Microsoft.XMLDOM")
  'You must set the content type so that the returning document will
  'be identified as XML.
  Response.ContentType = "text/xml"
  'Load the posted XML document.
  strObject= Request.ServerVariables.Item ("HTTP_POST")
  'Remove /
  strObject= Right(strObject, len(strObject)-1)
  'Remove HTTP...
  strObject= Left(strObject, instr(1, strObject, " ") )
  strMethod= Request.ServerVariables.Item ("HTTP_SOAPAction")
  'Strip off URL.
  strMethod=Right(strMethod, len(strMethod)-instr(1, strMethod,"#"))
  'Use the load method to get the XML sent from the client out of the
  'request object.
  objXMLDocRequest.load Request
  'Now that you have the XML data, use the values in the XML
  'document to set the local variables.
  strCustomerNumber=objXMLDocRequest.SelectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/CustomerNumber").Text
  strItem = objXMLDocRequest.SelectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Item").Text
  strQuantity = objXMLDocRequest.SelectSingleNode _
      ("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Quantity").Text
  'Using the name of the object passed in the header, we will
  'instantiate the object.
  'Because we do not actually have an object called objOrderPO, we
  'will comment out the next line of code.
  'Set objOrderPO = _
  '    server.CreateObject (Request.ServerVariables.Item(strObject))
  'Call the correct method passing in the parameters.
  Select Case strMethod
      Case "UpdatePO"
  'Because we do not actually have an object called objOrderPO, we
  'will comment out the next two lines of code. We are also adding
  'dummy values for the strReturn and strOrderID variables.
      'strReturn=objOrderPO.UpdatePO _
      '(strCustomerName, strItem, strQuantity, strOrderID)
      strReturn="0"
      strOrderID="100"
      Case "DeletePO"
      strReturn=objOrderPO.DeletePO _
           (strCustomerName, strItem, strQuantity, strOrderID)
  End Select
  'Create XML that is going back to the client using a string.
  objXMLDocResponse.LoadXML _
      "<SOAP-ENV:Envelope xmlns:SOAP-ENV =" & _
  "'http://schemas.xmlsoap.org/soap/envelope'" & _
      " xmlns:xsi='http://www.w3.org/1999/XMLSchema/instance' " & _
      " xsi:schemaLocation=" & _
      "'http://www.northwindtraders.com/schemas/NPOSchema.xsd'>" & _
      "<SOAP-ENV:Body xsi:type='northwindBody'><UpdatePO>" & _
      "<OrderID>" & strOrderID & "</OrderID>" & _
      "<return>" & strReturn & _
      "</return></UpdatePO></SOAP-ENV:Body>" & _
      "</SOAP-ENV:Envelope>"
  'Return the XML.
  Response.Write objXMLDocResponse.xml
  </SCRIPT>

In this example, you can see how tightly bound the client-side code is to the object that is being called. The client-side object has to build an XML document that contains the correct parameters and also has to create a header with the right object and method names.

XML Parser Version 2.6 and Version 3.0

The XML parser version 2.6 and version 3.0 extend the older version that came with Internet Explorer 5. The XML parser version 2.6 is a separate DLL (Msxml2.dll) that can be installed in addition to the original XML parser. Various options are available for running the two DLLs together. You can check the XML SDK 2.5 (which can be viewed on Microsoft's Web site) for more information about the DLLs. Version 3.0 of the parser is installed as a new DLL (Msxml3.dll) with new version-dependent CLSIDs and ProgIDs to protect those applications that use Msxml.dll or Msxml2.dll and allow you to choose the version of the parser to use in your code.

Version 2.6 comes with five additional XML document objects: XMLDOMDocument2, XMLDOMSchemaCache, XMLDOMSelection, XMLDOMXSLProcessor, and XMLDOMXSLTemplate. We will discuss the XMLDOMXSLTemplate and XMLDOMXSLProcessor objects in Chapter 12 when we discuss XSL. Version 3.0 doesn't add any new features to version 2.6. Thus, we'll have a detailed discussion about the XMLDOMDocument2, XMLDOMSchemaCache, and XMLDOMSelection objects in the following section.

In this chapter, you reviewed the majority of the XML DOM objects. These objects allow you to manipulate an XML document in virtually any way that might be required by an application. These objects provide a powerful set of tools that allow you to begin building complete XML applications. You can also use the DOM objects to both send messages to and receive messages from a Web server, which allows you to create SOAP messages as shown in the example in this chapter. In the next chapter, we will look at XSL and learn how the DOM can be used with it.