XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

DOM for Web Services, Part 2
by Faheem Khan | Pages: 1, 2, 3

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 node2
Text node 3
Processing instruction node 7
Comment node8
Document node9

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.

Pages: 1, 2, 3

Next Pagearrow