Menu

DOM for Web Services, Part 2

November 11, 2003

Faheem Khan

Using MSXML to Generate Web Service User Interfaces

In the first article of this series, I discussed web service usage models and described XML processing requirements in web services. I also introduced W3C Document Object Model (DOM) and two of its popular implementations, MSXML and Xerces.

This second article discusses MSXML in detail. I use MSXML DOM to perform a WSDL to HTML transformation in order to generate a user interface for a web service. I will demonstrate the use of MSXML on both client and server sides. On the client side, I'll use JavaScript inside a browser and, on the server side, MSXML inside an ASP.NET page.

While explaining the WSDL to HTML transformation, I'll cover XML processing as well as HTML authoring with MSXML. This will provide an opportunity to demonstrate the following XML processing issues, as well as some DOM-HTML authoring techniques:

XML processing issues:

  1. Creating a new DOM object
  2. Loading an XML document into the DOM object
  3. Handling errors while parsing a DOM Document
  4. Finding the root element
  5. Working with the name of an element
  6. Working with namespaces
  7. Reading the local name of an element node
  8. Getting the children of an element
  9. Checking the type of a node
  10. Reading an attribute value
  11. Visiting a particular child of an element
  12. Reading textual content of an element
  13. Visiting an element with a particular attribute value

MSXML offers some proprietary, non-standard, DOM-related features. As the focus in this series of articles is mainly on standard W3C DOM I will mention some of the proprietary features of MSXML DOM in this article, but will not go into their details.

In order to try the code developed for this article, you will need MSXML version 4.0 installed on your machine. You just need to install MSXML once on your machine: your browser and .NET visual development environment will both work using the same MSXML installation.

Let's start with our WSDL to HTML transformation example.

MSXML with JavaScript

Have a look at Listing 1, which is a WSDL file. Listing 2 is an HTML file, which uses MSXML inside JavaScript code to process Listing 1 and generate its HTML-based presentation. The body element in Listing 2 simply calls the ProcessWSDLDocument() method, which generates the complete HTML structure of Listing 3. Let's see how.

The document object in a browser

W3C DOM contains a core module which defines interfaces for all the different types of XML DOM nodes (e.g. element, attribute, text, comment etc.). W3C DOM also defines an HTML module which defines HTML-specific interfaces (e.g. the HTMLDocument interface for an HTML document, the HTMLHtmlElement interface for HTML element, HTMLBodyElement interface for the body element, the HTMLTableElement interface for the table element, etc.).

The HTML module uses the functionality of the core module. All interfaces in the HTML module inherit from some interface in the core module. For example, the HTMLDocument interface in the HTML module inherits from the Document interface of the core module and the HTMLElement interface module inherits from the Element interface.

Internet Explorer browsers (IE) have their own DOM implementations, which do not accurately follow W3C DOM. You can use the browser object model to author an HTML page dynamically from JavaScript code. IE (version 3.0 and later) provides an object named "document". The document object represents the entire HTML document being displayed inside the IE window. You don't need to instantiate this object, as its instance is already available. You can directly call its methods to create elements.

Creating a new paragraph

The document object exposes many methods, some of which follow standard W3C DOM, while others are Microsoft specific. The document object exposes four very useful standard W3C methods, namely createElement(), createTextNode(), appendChild() and insertBefore().

Moreover, the HTML module in W3C DOM defines HTML attributes as standard properties for element nodes. The document object allows us to create element nodes and use their properties. When you set a property of an HTML element node, the property will appear as an attribute value in the resulting HTML code.

So let's create a new paragraph (a p element) and set text alignment property. The first line in the ProcessWSDLDocument()method in Listing 2 (var para = document.createElement("p");) creates a new p element. The second line (para.align="center";) sets its text alignment as center-aligned. This code will generate the following HTML:

<p align="center" />

We have created a new p element by calling the document.createElement() method. The newly created p tag belongs to the document object, but we have still not attached it at a particular place in the document.

The third line (document.body.appendChild(para);) appends the newly created p element into the HTML document body. Notice that you can directly fetch the body of the HTML document by referencing document.body.

The object that the document.body returns exposes the methods defined by the HTMLBodyElement interface of W3C DOM. Once we have the HTML body object, we can call its appendChild() method to append the p element to the body. We have simply attached the p element to the document body, without specifying a particular place where the p element needs to be inserted. The result is that the p element gets attached as the last existing child of the HTML body:

<html>
   <head></head>
   <script></script>  
   <body>
      <p align="center" />
   </body>
</html>

Listing 2 contains the hard coded html, head, and script elements, etc., which automatically appear in the document object. Whatever we author using the interfaces exposed by different DOM objects is added to the already available elements.

Creating a new DOM object

The next step is to load a WSDL file into an XML DOM object. For this purpose, we will need to instantiate an MSXML DOM document as an ActiveX object. The var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.4.0"); line in the ProcessWSDLDocument() method creates a new MSXML DOM object named xmlDoc. The xmlDoc object exposes the IXMLDOMDocument interface of MSXML.

The IXMLDOMDocument interface is equivalent to the Document interface of standard W3C DOM. The IXMLDOMDocument interface in MSXML inherits from the IXMLDOMNode interface, which corresponds to the generic Node interface of W3C DOM. All the other W3C interfaces inherit from the DOM Node interface.

So now we have two DOM objects in the JavaScript of our Listing 2. The first is the browser document object, which represents the HTML page that we are authoring. The second is the xmlDoc XML DOM object in which we will shortly load our WSDL file.

Loading an XML document into the DOM object

The MSXML document object is now ready and we can load a WSDL file into the DOM object. We can use the xmlDoc.load(xmlFileName) method for this purpose.

The load() method is part of the IXMLDOMDocument interface in MSXML. However, note that W3C DOM Level 2 does not define any mechanism for loading an XML document into a DOM object. Therefore, implementations define their own methods of loading XML files in DOM objects.

Note that DOM level 3 defines standard methods for loading an XML file into a DOM object. MSXML does not implement DOM level 3. I will discuss DOM level 3 in the next article of this series.

The xmlDoc.async="false" property is used in conjunction with xmlDoc.load() method call. The xmlDoc.load() method can work synchronously or asynchronously. The default behavior is asynchronous. Asynchronous loading means that the load() method will return immediately without waiting for the XML-to-DOM loading process to complete. The synchronous way of loading means that the load() method will not return until the XML loading into the DOM object is complete.

We are using synchronous loading (meaning the entire document will be loaded into DOM before you can start to process the file), so we have set the async property of the xmlDoc object to "false".

If you are downloading the HTML file of Listing 2 from a web server and want to download the WSDL from the server as well, you may want to specify a complete URL instead of just the WSDL file name (e.g. xmlDoc.load("http://myServer/WSDL_Service.wsdl")).

Handling errors while parsing a DOM Document

The IXMLDOMDocument interface has a property named parseError, an object that exposes IXMLDOMParseError interface. This is an MSXML-specific interface. An IXMLDOMParseError object contains information about the errors that occurred during parsing. The property errorCode wraps an error code, which is zero if there was no error.

Finding the root element

Our WSDL file of Listing 1 is now loaded in the xmlDoc IXMLDOMDocument object. Now let's proceed with the parsing of our WSDL file. The documentElement property of the IXMLDOMDocument interface returns the root node of the XML document. So the definitionsElement = xmlDoc.documentElement line in Listing 2 stores the root node object in a variable named definitionsElement.

Working with the name of an element

Our next task is to read the name of the root node and check whether the name of the root node is "definitions".

The IXMLDOMNode interface contains a property named nodeName. The nodeName property is available in the IXMLDOMElement interface, as it inherits from the IXMLDOMNode interface.

The nodeName property, according to W3C DOM, returns the qualified name of an element. The qualified name of the element contains the complete name including the namespace prefix as well as the local name of the element.

In order to demonstrate the use of qualified names and their handling with W3C DOM, we have provided the WSDL file of Listing 4, which is the equivalent form of Listing 1, but only differs in the use of XML namespace prefixes. The local name of the root element in Listing 4 is wsdl:definitions, instead of just definitions as it was in Listing 1.

The definitionsElement.nodeName property returns the complete qualified name. So for Listing 1, the definitionsElement.nodeName will return "definitions", while for Listing 4, the same method call will return "wsdl:definitions".

That's why if we use definitionsElement.nodeName property to check the name of the root node, we will be relying on the namespace prefix in the WSDL file. We cannot be sure what prefix the author of the WSDL file will use. Therefore, it is not appropriate to use the complete qualified name for checking the name of an element. We should rather check that:

  1. The namespace URI associated with the root definitions element should be that of a WSDL file (http://schemas.xmlsoap.org/wsdl/).
  2. The local name of the root element node is "definitions".

Working with namespaces

W3C DOM Level 2 does not have any method to find the namespaceURI associated with a particular prefix. The generic Node interface in W3C DOM has a property named namespaceURI, which tells the namespace associated with a particular node, but it only looks for the namespace URI in the particular node and does not perform any programmatic lookup (e.g. check the namespaces declared with parents and grandparents of a particular node).

The IXMLDOMNode interface in MSXML has a property named namespaceURI, which dynamically finds the namespace URI associated with a particular node. But we would like to stick with standard DOM, therefore we will not use this property.

Take a look at the recursive method named getNamespaceURI() in Listing 2. This function takes an element node and a namespace prefix as parameters and returns the namespace URI associated with the prefix. If you pass null as namespace prefix as the second parameter then the getNamespaceURI() method will return the default namespace of the element node. It starts from the element node and progressively looks for the namespace URIs associated with the parent, grandparent, etc. The method stops its search when it has either found the required namespace URI or it hits the document element.

The definitionsNamespacePrefix = getPrefix(definitionsElement.nodeName) line reads the namespace prefix associated with the root definitions element. The getPrefix() method takes a qualified name of a node and returns the namespace prefix.

After reading the prefix, we pass it to the getNamespaceURI() method, which returns the namespace URI associated with the prefix. We have stored the namespace URI value in a variable named definitionsNamespaceURI. Once we have the namespace URI, we can simply compare it to the WSDL namespace by using an definitionsNamespaceURI != "http://schemas.xmlsoap.org/wsdl/" condition.

Reading the local name of an element node

The generic Node interface in W3C DOM contains an attribute named localName. The localName property always returns the local part of the qualified name. The definitionsElement.localName property call will return "definitions" for both Listings 1 and 4.

Unfortunately MSXML 4 does not implement the localName property. Instead MSXML implements its own baseName property, which returns the local part of the qualified name. As our focus is only on the standard W3C features, we are trying to avoid the use of Microsoft-specific methods for WSDL processing.

Listing 2 includes a method named getLocalName(), which takes a complete qualified name and returns the local part of the qualified name. In order to check the name of the root element, we have checked getLocalName (definitionsElement.nodeName) != "definitions" condition. The definitionsElement.nodeName returns the complete qualified name, which is passed as a parameter to the getLocalName() method. The getLocalName() method returns the local part of the qualified name.

Getting the children of an element

We have checked that the root element of the WSDL file is definitions and the namespace URI associated with the definitions element is http://schemas.xmlsoap.org/wsdl/. The next step is to check the service child element of the definitions element.

The IXMLDOMElement interface has a standard W3C DOM method named getElementsByTagName(), which takes a tag name as a parameter. This method searches all descendant (child, grandchild, grand grand child etc.) nodes of the element node and returns a list of those nodes whose tag names match the tag name that you passed to the getElementsByTagName() method call.

The getElementsByTagName() works on qualified names. This means that the method will include an element in the list only if both namespace prefix and the local name of an element match with the tag name that you provided to the method call. That's why this method is not suitable for our purpose, as we don't want to assume that the author of the WSDL file will choose a particular prefix for the WSDL namespace.

The Element interface in W3C DOM contains another method named getElementsByTagNameNS(). This method takes two parameters namely, a namespace URI and the local name of an element. This method searches all descendant nodes of the element node object with matching namespace URI and local names. This method would have worked perfect for our purpose, however unfortunately MSXML does not implement this method.

Therefore, in order to find all the service elements that are direct children of the definitions root element, we have to write some simple logic. In Listing 2, we have first called the definitionsElement.childNodes property. The childNodes property belongs to the IXMLDOMNode interface and returns a list of all child nodes of an element.

The list of child nodes is in the form of an object that exposes an IXMLDOMNodeList interface. The IXMLDOMNodeList interface corresponds to the NodeList interface in W3C DOM.

The IXMLDOMNodeList interface provides methods to iterate through the list to find element matching certain criteria. So we have to run a loop and check all nodes with node type "element" and with local name "service". The for-if combination block after the definitionsElement.childNodes line in Listing 2 performs this job.

This for-if combination block does not only check the local name of a node, it also checks the type of a node. Let's see why and how.

Checking the type of a node

It is not necessarily the case that the list of child nodes that the childNodes property returns are of "element" type. They may be of other types of nodes as well (e.g. text nodes, comment nodes, etc.). Therefore, in addition to checking that the local name of a child node is "service", we also have to check that the type of the child node is "element".

When we find the first node whose local name is "service", and type of the node is "element", we quit the loop.

In order to check the type of a node, we have used the nodeType property of the IXMLDOMNode interface. This is a standard W3C DOM property, which returns an integer value of a node. W3C DOM has defined integer values for 12 different types of nodes. Some of the commonly used types of nodes include the following:

Type of node Integer value
Element node 1
Attribute node 2
Text node 3
Processing instruction node 7
Comment node 8
Document node 9

So we can simply call the nodeType property of any node and compare it to an integer value in order to check what type of node it is. The for-if combination block brings all the nodes of type "element" and local names "service".

We have passed each of the service elements to a method named getServiceTable(). The getServiceTable() method will process the service element and author a complete HTML table representing the presentation logic of the service element. Therefore, we will get one complete HTML table for each service element in the WSDL file. Let's see how the getServiceTable() method works.

Authoring tables

The first thing that the getServiceTable() method in Listing 2 does is that it creates a new table object. The document.createElement("table") method creates a new table element. The next few lines of code in this method set parameters related to the visual appearance of the table (border, background color, cell padding, etc.). The result of these lines is an empty table element as shown below:

<table border="6" bgColor="#F0F8FF" cellPadding="4" cellSpacing="5" borderColor="#D2B48C"/>

After creating the table object, we need to create a tbody element. The var tBody = document.createElement("tbody") authors a new standalone table body. You will now attach the newly created table body to the table element that you have already authored. The table.appendChild(tBody) method call performs this job. Now the HTML table looks like the following:

<table border="6" bgColor="#F0F8FF" cellPadding="4" cellSpacing="5" borderColor="#D2B48C">
    <tbody/>
</table>

Next, we need to author all the child elements of the tbody element. Look at the tbody.appendChild(createRow("Name of service", serviceElement.getAttribute("name"))) line. This line of code performs three important things that we need to discuss separately:

  1. Reads the name attribute value of the service element.
  2. Creates a new row of an HTML table.
  3. Appends the newly created row to the tbody structure.

Let's discuss these three things in detail before proceeding further.

Reading an attribute value

Notice form Listing 1 that the service element has a name attribute, whose value is "CityPortal". This is the name of the web service that we are trying to transform into an HTML file. We would like to read the value of the name attribute and include the value in the HTML code.

The serviceElement.getAttribute("name") method call returns the name attribute value of the service element. The getAttribute() method is a standard W3C DOM method that belongs to the IXMLDOMElement interface.

Creating a single row of an HTML Table

Listing 2 includes a method named createRow(), which takes two strings as parameters and returns a single row of an HTML table, with the two strings set as textual content inside two columns of the row.

For example, if you pass "string1" and "string2" as the two parameters to the createRow() method call, the createRow() method will return the following structure:

<tr><td><b>string1</b></td><td>string2</td></tr>

Therefore, when we make the createRow("Name of service", serviceElement.getAttribute("name")) method call, we are passing "Name of Service" string as the first string and the actual name of the service (CityPortal) as the second string. The resulting HTML that the createRow() method will author and return is shown below:

<tr><td><b>Name of Service</b></td><td>CityPortal</td></tr>

Let's see how the createRow() method works.

Creating text nodes and adding textual content to an element

Inside the createRow() method, we have created a table row (tr) element, a table cell (td) and a b element using the document.createElement() method.

After creating the three HTML elements, we have called the document.createTextNode() method to create a new text node.

The creation of text nodes is not different from the creation of new elements that we have already discussed. You create a new text node using the document.createTextNode() method and pass the text of the new node along with the createTextNode() method call. Next you call the appendChild() method of the element to which you want to attach the text node and pass the newly created text node as a parameter to the appendChild() method call.

After creating a text node, we first call the appendChild() method of the bold node to attach the text node to the bold element, thus effectively wrapping the label content inside a bold element, as shown below:

<b>Name of Service</b>

Next we call the appendChild() method of the cell node to attach the complete b node to the cell node:

<td><b>Name of Service</b></td>

Next we call the appendChild() method of the row (tr) node to attach the complete cell (td structure) to the row node:

<tr><td><b>Name of Service</b></td></tr>

We have authored one cell of a row. The next few lines of the createRow() method author the second cell in a similar manner and append the second cell to the same row. The complete row that the createRow() method authors looks like the following:

<tr><td><b>Name of Service</b></td><td>CityPortal</td></tr>

The last line of the createRow() method returns the completed row node back to the calling application.

Inserting child nodes

Notice that in the createRow() method, we have called the appendChild() method of the row node twice, that is, once for each cell of the row. The cells appear in the same sequence that you used to call the appendChild() method. The appendChild() method always inserts a new row as the last child of the parent.

What if you want to append a child before another child that you have already appended to a parent element node? The IXMLDOMNode interface has a method named insertBefore(), which takes two parameters: the name to be inserted and the node before which the new node is to be inserted. For example, the insertBefore() method call in the following code will insert row1 before row2:

cell.appendChild(row2);
cell.insertBefore(row1, row2);

If you pass null as the second parameter to the insertBefore() method, the new incoming node will be inserted as the last child of the parent node. Therefore, cell.insertBefore(lastRow, null) and cell.appendChild(lastRow) have identical effects.

Appending a table row to the table body

So we have created one row of our table. Let's return to our discussion on tBody.appendChild(createRow("Name of Service", serviceElement.getAttribute("name"))) method inside the getServiceTable() method. The createRow() method, as we have already explained, authors and returns a complete table row. The tBody.appendChild() method appends the table row to the table body.

Visiting a particular child of an element

Our next task is to fetch the documentation child element of the service element. For this purpose, we have used the serviceElement.childNodes property call, which returns a list of all child nodes of the service element. A for loop follows, which checks all the child nodes of the service element and finds a child node of type "element" and whose local name is documentation. When it finds such a child element, it creates a row of the service table and writes the documentation in the row.

Reading textual content of an element

Notice that in order to read the textual description of the service documentation, we have used the documentationElement.firstChild.nodeValue property.

The documentationElement.firstChild property returns the first child of the documentation element. Here we are assuming that the documentation element contains only one child node, which is of type "Text" and wraps the entire textual description of the service.

The documentationElement.firsatChild.nodeValue property returns the value of the text node. The value of a text node, by W3C DOM definition, is the actual textual content of the text node. We have simply passed the textual content to the createRow() method, which creates a row of textual description in our table.

The next step is to read the port element child of the service element. The port element contains the type of the port (type means the abstract definition of the port).

We have written a getChild() helper method in Listing 2, which takes a parent node object and the local name of a child element and returns the first matching child node object. Please have a look at the getChild() method implementation in Listing 2. We have already explained all concepts that we have used to implement this method.

The getChild() method fetches the port element child of the service element.

Visiting an element with a particular attribute value

The port element has an attribute named "name". The name attribute value matches with the name attribute value of a portType element. We want to reach the portType element whose name attribute value matches with the name attribute value of the port element. Notice from Listing 1 that portType and service elements are siblings (immediate children of the definition element). So in order to fetch the required portType element, we have to perform the following tasks:

  1. Read the "name" attribute value of the port element.
  2. Fetch a list of all siblings of the definitions element.
  3. Find a service sibling named "portType" whose name attribute value matches with the "name" attribute value of the port element (from point 1).

The portElement.getAttribute("name") method call in Listing 2 performs the first task.

The serviceElement.parentNode.childNodes line performs the second task; that is, it retrieves a list of all siblings of the service element, including the service element itself as well. Note that W3C DOM does not have any method to fetch the list of all siblings of an element directly. That's why, in order to fetch the list of siblings, we first have to fetch the parent of a node and then all children of the parent.

The for-if combination just after the serviceElement.parentNode.childNodes line in Listing 2 performs the third job. The for (var i=0; i<serviceSiblings.length;i++) loop checks each of the service siblings one by one. The if statement inside the for loop checks whether the service sibling is a portType element and whether its name attribute value matches with the name attribute value of the portType element.

So when the if statement becomes true, we are sure we have found the required portType element.

Inside the for-if block, we have simply passed the portType element to a method named attachPortTypeSetOfRows(), which performs the rest of the XML processing to author table rows corresponding to the port type. Note that we have also passed the tBody node object to the attachPortTypeSetOfRows() method, so that the method authors the table rows and attaches the rows to the same table body that contains some of the rows that have already authored.

The portType element in Listing 1 contains two operation child elements. The portType element may contain any number of operation child elements. Each operation element is actually a web service method. Therefore, we need to generate presentation logic (i.e., HTML code) for each of the operation elements.

The attachPortTypeSetOfRows() method contains a loop, which reads each of the operation child elements of the portType element and passes the operation element to a method named getOperationSetOfRows(), which will author the table rows corresponding to a single operation element.

Inside the attachOperationSetOfRows() method, we first create a row corresponding to the name attribute value of the operation element. The name attribute value is actually the name of the operation.

We then read documentation child of the operation element and create a row corresponding to the documentation (textual description) of the operation.

After that, we try to fetch an input child element of the operation element. There can be at a maximum of one input child element of the operation element. If there is an input element, we read its message attribute value and store the attribute value in a variable named inputMessage.

The message attribute value matches with the name attribute value of a message element. Notice from the WSDL file of Listing 1 that all message elements are direct children of the root definitions element. Therefore, in order to fetch the required message element, we first move to the root definitions element (OperationElement.parentNode.parentNode) and then fetch all the children of the definitions element (OperationElement.parentNode.parentNode.childNodes). Once we have all the children of the definitions element, we check them one by one, by using a for-if combination, to see if it is the required message element whose name attribute value matches with the name attribute value of the input element.

When we reach inside the for-if block, we are sure we have found the required message element, so we simply pass the message element to a method named attachMessageRows(). The attachMessageRows() method authors all the table rows associated with a message.

Authoring a data entry form

Now inside the attachMessageRows() method, we again do some table authoring. First we create a new form element and set its action and method attributes.

<form action=" http://localhost/DOM4WS/SoapRequestGenerator" method="GET" />

We then fetch a list of all child nodes of the message element. Then inside a for loop, we pass all part child nodes one by one to a method named attachPartRow(). The attachPartRow() method authors a table row corresponding to a single part of the message element.

When the for loop finishes, we know we have authored all the message rows corresponding to all part elements. For each part element, the attachPartRow() method authors a single row with two cells. The first cell contains the "name" attribute of the part and the second cell contains a text entry input box for the user to enter data related to the part element. We have already explained the HTML authoring process; you create an HTML element using document.createElemet() method, set its attributes (if any) and then append the newly created HTML element to its parent node. The attachPartRow() method appends the a table row to the form element that we created in the last step. The form element now looks like this:

<form action=" http://localhost/DOM4WS/SoapRequestGenerator" method="GET">
    <tr>
        <td>EnterCityName</td>
        <td><input type="text" /></td>
    </tr>
    <!--Other possible rows, one for each part of the message-->
</form>

After calling the attachPartRow() method for each part element, the attachMessageRows() method authors an HTML Submit Query button. The user will press this button after entering data in the text entry boxes for each part of the message and the user input will be passed to the HTML file mentioned in action attribute value of the form element. Now the complete HTML form representing a single WSDL operation will look like this:

<form action=" http://localhost/DOM4WS/SoapRequestGenerator" method="GET">
    <tr>
        <td>EnterCityName</td>
        <td><input type="text" /></td>
    </tr>
    <!--Other possible rows, one for each part of the message-->
    <tr>
        <td></td>
        <td><input type="submit" value="Submit Query" /></td>
    </tr>
</form>

A web service may contain more then one operation elements. The attachMessageRows() and attachPartRow() methods work together to author an HTML form for each operation. We then embed the completed form element inside the table body.

MSXML with ASP.NET

Listing 5 demonstrates the use of MSXML inside an ASP.NET page. You will need to place Listing 5 as a WebForm1.aspx file in an ASP.NET project virtual directory. You will also have to add a COM reference to MSXML4.dll file (which implements MSXML version 4) to the ASP.NET project. The .NET Visual Studio provides a graphical environment for adding COM references to your ASP.NET projects. We have provided a link to an article in the resources section that describes how to add COM references in ASP.NET projects.

ASP.NET code of Listing 5 is almost the same as the "JavaScript" code of Listing 2 except a few minor differences as noted below:

Listing 5 is server side code, while Listing 2 is client side code. Therefore, you will notice syntax-related differences between Listing 2 and Listing 5. On server side we can't use the browser's document object, so we have used a separate XML DOM document object for HTML authoring. Therefore, you will find two DOM document objects (named htmlDoc and xmlDoc) in Listing 5. The htmlDoc object is for HTML authoring, while xmlDoc object is for WSDL processing.

We have defined the htmlDoc object as a global object in Listing 5, so that it is accessible at all places just like the document object in client side JavaScript code (Listing 2).

Next time we will demonstrate how to use Xerces, the popular Java-based implementation of W3C DOM. We will also discuss the new features that DOM level 3 introduces.

Resources

  • If you want to try the code from this article, you may download the code.zip file. which contains all the source code and a self explanatory Readme.txt file. It also contains a few WSDL and HTML file pairs, generated using Listing 5 of this article.
  • Check out the DOM main page at W3C.
  • Download MSXML4.0 from the Microsoft site.
  • This article provides an introduction to MSXML4.0.
  • Read this Introduction to Web services and WSDL.
  • This article explains how to provide COM references to ASP.NET projects.
  • This book explains XML application development using MSXML4.0.