DOM for Web Services, Part 2
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:
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.
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.
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.
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.
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.
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")).
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.
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.
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:
definitions element should be that of a WSDL file (http://schemas.xmlsoap.org/wsdl/).
"definitions".
|
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.
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.
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.
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.
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:
name attribute value of the service element.
tbody structure.Let's discuss these three things in detail before proceeding further.
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.
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.
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.
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.
|
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.
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.
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.
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:
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.
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.
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
|
XML.com Copyright © 1998-2006 O'Reilly Media, Inc.