DOM for Web Services, Part 2
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:
- Creating a new DOM object
- Loading an XML document into the DOM object
- Handling errors while parsing a DOM Document
- Finding the root element
- Working with the name of an element
- Working with namespaces
- Reading the local name of an element node
- Getting the children of an element
- Checking the type of a node
- Reading an attribute value
- Visiting a particular child of an element
- Reading textual content of an element
- 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:
- The namespace URI associated with the root
definitionselement should be that of a WSDL file (http://schemas.xmlsoap.org/wsdl/). -
The local name of the root element node is
"definitions".