DOM for Web Services, Part 2
November 11, 2003
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
definitions
element should be that of a WSDL file (http://schemas.xmlsoap.org/wsdl/
). - 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:
- Reads the
name
attribute value of theservice
element. - Creates a new row of an HTML table.
- 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:
- Read the "name" attribute value of the port element.
- Fetch a list of all siblings of the definitions element.
- 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
|