The XML Document Object Model
The XML Document Object Model (DOM) is a platform-neutral and language-neutral interface that allows developers to create applications and scripts to access and update the content, style, and structure of XML documents. The XML DOM is based on the W3C DOM, which is a recommended specification that has been released by the DOM Working Group (WG) in several stages. The DOM Level 1 specification introduces the features that can be used to manipulate the content and structure of HTML and XML documents. The W3C is currently working on the DOM Level 2 specification. This specification extends DOM Level 1 with many new features. Microsoft Internet Explorer 5 fully supports the W3C Level 1 DOM specification.
NOTE
For more information about the features included in each DOM level, visit the W3C Web site at http://www.w3.org/dom.
In addition to the XML DOM support included with Internet Explorer 5, Microsoft XML parser version 2.6 and version 3.0 have been released that support several extensions of the XML DOM beyond the current W3C specification, including BizTalk schemas, XPath, and XSL Transformations (XSLT). We will begin this tutorial with a discussion of the implementation of the XML DOM in Internet Explorer 5 and end with a discussion of the new XML parser and the additional functionality that it adds.
The implementation of the XML DOM in Internet Explorer 5 consists of a set of objects that can be used to load an XML document. Once the document is loaded, you can parse, navigate, and manipulate the information in the XML document. The DOM can also be used for retrieving information about the document.
There are four main objects included with Internet Explorer 5's implementation of the XML DOM: XMLDOMDocument, XMLDOMNode, XMLDOMNodeList, and XMLDOMNamedNodeMap. In addition, sixteen other objects are part of Internet Explorer 5's implementation of the XML DOM. All of these objects have properties and methods that you can use to gather information about the document (including its structure) and to navigate to other object nodes within a document tree. A node is a reference to any object that can exist in a document hierarchy. The ability to access different nodes of the document tree is a function that is also available using XPath and XPointer. Twelve different types of nodes are available in the DOM: element, attribute, text, CDATA section, entity reference, entity, processing instruction, comment, document, document type, document fragment, and notation. An interface exists for each of these node types that allows you to gather and manipulate information on the node. The most common node types are the element, attribute, and text nodes.
NOTE
Attributes are not actually child elements of any node in the tree, so they have a special programming interface called IXMLDOMNamedNodeMap.
The W3C DOM specification defines two types of programming interfaces: fundamental and extended. The fundamental DOM interfaces are required when writing applications that manipulate XML documents. The extended interfaces are not required, but make it easier for developers to write applications. The Internet Explorer 5 DOM implements both the fundamental and extended interfaces. In addition, it provides other interfaces to support Extensible Stylesheet Language (XSL), XSL patterns, namespaces, and data types.
For script developers, the most important object in the Internet Explorer 5's implementation of the XML DOM is the XMLDOMDocument object, which allows developers to navigate, query, and modify the content and structure of an XML document. This object implements the IXMLDOMDocument interface. We will look at this object first.
To navigate and get a reference to an XML document, you need to use the XMLDOMDocument object. Once you actually get a reference to the document, you can begin to work with it. The XMLDOMDocument object implements the IXMLDOMDocument interface.
Getting a reference to an XML document
Depending on the programming language you are using, you can get a reference to an XML document in several ways.
In Microsoft JScript, you can get a reference as follows:
var objXMLdoc = new ActiveXObject("Microsoft.XMLDOM);
objXMLdoc.load("http://www.northwindtraders.com/sales.xml");
|
In VBScript, the code for obtaining a reference appears as follows:
Dim objXMLdoc
Set objXMLdoc = CreateObject("Microsoft.XMLDOM")
objXMLdoc.load("http://www.northwindtraders.com/sales.xml")
|
In Microsoft Visual Basic, you should add a reference to Msxml.dll to your project by choosing References from the Project menu, and then choosing Microsoft XML version 2 from the References dialog box. The code to get a reference to an XML document appears as follows:
Dim objXMLdoc As DomDocument
Set objXMLdoc = New DomDocument
objXMLdoc.load("http://www.northwindtraders.com/Sales.xml")
|
You could also use the following code without setting the reference, though the above method is preferable:
Set objXMLdoc = CreateObject("Microsoft.XMLDOM")
objXMLdoc.load("http://www.northwindtraders.com/Sales.xml")
|
IXMLDOMDocument interface properties and methods
In the above examples, we used the load method to get a reference to an actual XML document. The following tables list the properties, methods, and events associated with the IXMLDOMDocument interface. Properties and methods that are extensions of the W3C DOM Level 1 specification will be marked with an asterisk (*) throughout this chapter.
NOTE
Code samples illustrating how to use the IXMLDOMDocument interface will be presented later in this chapter.
IXMLDOMDocument Properties
| Name | Description |
|---|---|
| async* | Downloads the XML document asynchronously if this property is set to true (the default). |
| attributes | Returns an XMLDOMNamedNodeMap object for nodes that can return attributes. |
| baseName* | Returns the name of the node with any namespace removed. |
| childNodes | Returns all children of the current node for nodes that are allowed children. |
| dataType* | Sets or returns the data type for an XML document node that uses a schema. For entity references, elements, and attributes, if a data type is specified in the schema it will return the data type as a string. If no value is specified, it returns null, and for all other nodes it returns string. Attempts to set the dataType property for nodes other than attribute, element, or entity reference are ignored. |
| definition* | Returns the node that contains the DTD or schema definition for the entity referenced. |
| doctype | Returns a reference to an XMLDOMDocumentType node containing a reference to the DTD or schema. |
| documentElement | Returns a reference to the outermost document element of an XML document. |
| firstChild | Returns a reference to the first child of the current node. |
| implementation | Returns a reference to the XMLDOMImplementation object for the document. |
| lastChild | Returns a reference to the last child node of the current node. |
| namespaceURI* | Returns the Uniform Resource Identifier (URI) for the namespace as a string. |
| nextSibling | Returns a reference to the next sibling node of the current node. |
| nodeName | Returns the name of the node. |
| nodeTypeString* | Returns the node type as a string. |
| nodeType | Returns the node type as a number. |
| nodeTypedValue* | Returns or sets the strongly typed value of the node. |
| nodeValue | Sets or returns the value of the node as text. Returns attribute value for attribute nodes. Returns the text within the CDATA section for CDATASection nodes. Returns the comment for comment nodes. Returns the processing instruction for processing instruction nodes. Returns the text for text nodes. For all other nodes, it returns null if you try to get the property and raises an error if you try to set the property. |
| ownerDocument | Returns the root of the document that contains this node. |
| parentNode | Returns the parent node of the current node for nodes that are allowed to have parents. |
| parsed* | Returns true if the current node and all of its descendants have been parsed and instantiated. |
| parseError* | Returns a reference to the XMLDOMParseError object that contains information about the last parsing error. |
| prefix* | Returns the element namespace prefix as a string. |
| preserveWhiteSpace* | Specifies if white space should be preserved. The default is false. |
| previousSibling | Returns a reference to the previous sibling node of the current node. |
| readyState* | Indicates the current state of an XML document. |
| resolveExternals* | Resolves the external entities, and the document is resolved against external DTDs, if this is true. The default is false. |
| specified* | Returns true if a node value is specified. Returns false if a node value is derived from a default value. (This is normally used only with attribute nodes.) |
| text* | Sets and returns the text content of the current node and all of its descendants. |
| url* | Returns the URL of the last successfully loaded XML document or returns null if the XML document was built in memory. |
| validateOnParse* | The document will validate on parsing when this property is set to true, but the parser will only check the document for being well formed if this property is set to false. Default is true. This property can be set or read. |
| xml* | Returns the entire XML content of the current node and all of its descendant nodes. |
IXMLDOMDocument Methods
| Name | Description |
|---|---|
| abort()* | Stops the asynchronous load if the async property is set to true and the document is loading. Any information that has been downloaded is discarded. If the readyState property is equal to COMPLETED, calling abort has no effect. |
| appendChild (newChild) | Appends newChild to the end of the child nodes list for the currently selected node. |
| cloneNode (deep) | Creates a clone node that is identical to the currently referenced node. If deep is set to true, all child nodes are also cloned. |
| createAttribute (name) | Creates an attribute node with the specified name. |
| createCDATASection (text) | Creates a CDATASection node containing text. |
| createComment (text) | Creates a comment node containing text. The comment delimiters (<!-- -->) will be inserted. |
| createDocumentFragment() | Creates an empty DocumentFragment node that is used to build independent sections of the XML document. |
| createElement (name) | Creates an instance of the specified element. |
| createEntityReference (name) | Creates an EntityReference node called name. |
| createNode (type, name, namespace)* | Creates any type of node using the specified type, name, and namespace parameters. |
| createProcessingInstruction (target, data) | Creates a new processing instruction. The target parameter provides both the target and the node name. The data parameter is the actual instruction. |
| createTextNode (text) | Creates a text node containing the text specified in the text parameter. |
| getElementsByTagName (name) | Returns a collection of child elements that have the specified tag name. If the name parameter is *, it returns all elements. |
| hasChildNodes() | Returns true if the node has any child nodes. |
| insertBefore (newNode, beforeNode) | Inserts a new node object called newNode into the list of child nodes for the current node to the left of the beforeNode or at the end if beforeNode is left out. |
| load (url)* | Loads an XML document from the specified URL. |
| loadXML (string)* | Loads a string that contains well-formed XML. |
| nodeFromID (value)* | Returns the node object that has an ID attribute matching the supplied value. |
| removeChild (node) | Removes the child node from the current node and returns it. |
| replaceChild (newNode, oldNode) | Replaces the child node oldNode with the node newNode. |
| save (destination)* | Saves the file to the specified destination. |
| selectNodes (pattern)* | Returns a node list object containing matching nodes. The pattern parameter is a string containing an XSL pattern. |
| selectSingleNode (pattern)* | Returns the first node object matching the pattern of a string containing XSL. |
| transformNode (stylesheet)* | Processes the node and its children using XSL pattern matching. The stylesheet parameter must be either an XMLDOMDocument node object or a node object in the XSL namespace. |
| transformNodeToObject (stylesheet, outputobject) | Transforms the node according to the XSL document and places the transformed document into the outputobject parameter. |
IXMLDOMDocument Events
| Name | Description |
|---|---|
| ondataavailable* | Occurs whenever data becomes available. When the async property is true, this event fires several times as data comes in. Using the readyState property, you can obtain information on the incoming data, including when all of the data has been downloaded. |
| onreadystatechange* | Fires whenever the readyState property changes. |
| ontransformnode* | Fires when a node is transformed using the TransformNode method of the node object. |
The XMLDOMNode object implements the IXMLDOMNode interface. This interface contains the following properties: attributes, baseName, childNodes, dataType, definition, firstChild, lastChild, namespaceURI, nextSibling, nodeName, nodeTypeString , nodeType, nodeTypedValue, nodeValue, ownerDocument, parentNode, parsed, prefix, previousSibling, specified, text, and xml. The methods associated with IXMLDOMNode are appendChild, clonenode, hasChildNodes, insertBefore, removeChild, replaceChild, selectNodes, selectSingleNode, transformNode, and transformNodeToObject. There are no events associated with the IXMLDOMNode interface.
Looking at these properties and methods, you can see that they're all included in the IXMLDOMDocument interface and have been defined above. The same methods exist in both interfaces because IXMLDOMNode is used as the base interface for building all W3C DOM objects except for IXMLDOMImplementation, IXMLDOMNodeList, and IXMLDOMNamedNodeMap, as illustrated in Figure 11-1.
Besides these interfaces, Internet Explorer 5 has three additional interfaces: IXTLRuntime, IXMLDOMParseError, and IXMLHTTPRequest. You can see all the interfaces, including those specific to Internet Explorer 5, in Figure 11-2.
Figure 11-1. The relationship between the W3C DOM object interfaces.
Figure 11-2. Internet Explorer 5 DOM interfaces.
Let's look at how to code some of the methods and properties that belong to the IXMLDOMNode interface.
NOTE
The following example will show you how to use some of the properties and methods of the IXMLDOMNode interface using Visual Basic. If you have access to Visual Basic, I highly recommend that you follow the examples. If you don't have Visual Basic, you can easily convert this example to a script sample. This example will print out the values of various properties to the Immediate Window.
To create the sample application, follow these steps:
- Open Visual Basic, create a standard EXE project, and change the name of the default form to frmDOMTest.
- Choose References from the Project menu, and add a reference to Microsoft XML, version 2.0.
- Add a command button to frmDOMTest called cmdNode with a caption Nodes.
- Add the following code to the click event handler of cmdNode:
Private Sub cmdNode_Click() Dim objXMLDoc As DOMDocument 'Create a node object that is a reference to the root object. Dim objRoot As IXMLDOMNode 'Create a node object that can be used to create a new node. Dim objNewNode As IXMLDOMNode Set objXMLDoc = New DOMDocument 'Turn off asynchronous load as we do not need it for this example. objXMLDoc.async = False 'Open the file shown below. objXMLDoc.Load ("c:\Books.xml") 'The documentElement will return the root element. Set objRoot = objXMLDoc.documentElement 'Begin printing out properties for the root. Debug.Print "XML for the root: " & vbCrLf & objRoot.xml Debug.Print "BaseName for the root: " & objRoot.baseName Debug.Print "Namespace prefix for the root: " & objRoot.prefix Debug.Print "DataType for the root: " & objRoot.dataType 'We will begin to walk through the document starting at the first 'child. Debug.Print "First Child XML for the root: " & vbCrLf & _ objRoot.firstChild.xml 'We will get the next child, which is two elements down from 'the root. Debug.Print "First Child of Child XML for the root: " & _ objRoot.firstChild.firstChild.xml 'Nextsibling will return a node on the same level, in this case 'the same level as the second element down from the root. Debug.Print "Second Child of Child XML for the root: " & _ objRoot.firstChild.firstChild.nextSibling.xml Debug.Print "Third Child of Child XML for the root: " & _ objRoot.firstChild.firstChild.nextSibling. _ nextSibling.xml Debug.Print "Fourth Child of Child XML for the root: " & _ objRoot.firstChild.firstChild.nextSibling. _ nextSibling.nextSibling.xml Debug.Print "Namespace URI for the root: " & _ objRoot.namespaceURI Debug.Print "Nodename for the root: " & objRoot.nodeName Debug.Print "NodeType for the root: " & objRoot.nodeType Debug.Print "NodeType String for the root: " & _ objRoot.nodeTypeString Debug.Print "NodeValue for the root: " & objRoot.nodeValue Debug.Print "parentNode for the root: " & _ objRoot.parentNode.xml 'Using XSL to get a single node Debug.Print "XSL selecting first child node of the item node: " & _ vbCrLf & objRoot.selectSingleNode("item/*").xml Set objNewNode = objXMLDoc.createNode(1, "test", "") objRoot.appendChild objNewNode Debug.Print "Root XML after appending: " & vbCrLf & objRoot.xml Set objNewNode = Nothing Set objRoot = Nothing Set objXMLDoc = Nothing End Sub
Notice that we first get a reference to the document object. Using this reference, we can get a reference to the XMLDOMNode object. Then we start navigating the nodes in the XML document. Finally, we create a node named test and append it as a child node to the root node. To test this application, let's create an XML document called Books.xml in the C:\ directory with the following XML:
<?xml version="1.0" ?> <northwind:BOOKS xmlns:northwind="www.northwindtraders.com/PO"> <item> <title>Number, the Language of Science</title> <author>Danzig</author> <price>5.95</price> <quantity>3</quantity> </item> </northwind:BOOKS> |
When you run the program and click the Nodes button, the results are as follows:
XML for the root: <northwind:BOOKS xmlns:acme="www.northwindtraders.com/PO"> <item> <title>Number, the Language of Science</title> <author>Danzig</author> <price>5.95</price> <quantity>3</quantity> </item> </northwind:BOOKS> BaseName for the root:BOOKS Namespace prefix for the root:northwind DataType for the root: First Child XML for the root: <item> <title>Number, the Language of Science</title> <author>Danzig</author> <price>5.95</price> <quantity>3</quantity> </item> First Child of Child XML for the root: <title>Number, the Language of Science</title> Second Child of Child XML for the root: <author>Danzig</author> Third Child of Child XML for the root: <price>5.95</price> Fourth Child of Child XML for the root: <quantity>3</quantity> Namespace URI for the root: www.northwindtraders.com/PO Nodename for the root: northwind:BOOKS NodeType for the root: 1 NodeType String for the root: element NodeValue for the root: parentNode for the root: <?xml version="1.0"?> <northwind:BOOKS xmlns:northwind="www.northwindtraders.com/PO"> <item> <title>Number, the Language of Science</title> <author>Danzig</author> <price>5.95</price> <quantity>3</quantity> </item> </northwind:BOOKS> XSL selecting first child node of ITEM: <title>Number, the Language of Science</title> Root XML after appending: <northwind:BOOKS xmlns:northwind="www.northwindtraders.com/PO"> <item> <title>Number, the Language of Science</title> <author>Danzig</author> <price>5.95</price> <quantity>3</quantity> </item> <test/></northwind:BOOKS> |
Notice that the test element was inserted as the last child of the root, which is what we would have expected. Once this element is inserted, you can add text values or make other changes.
NOTE
Though we have not discussed XSL yet, we used XSL to get a single node in the previous application. XSL defines the location of elements using the XPath syntax. We'll discuss XSL in detail in Chapter 12. In this chapter, we will use the XPath syntax with the selectSingleNode method.
Several methods and properties belonging to the document object will return other objects in the hierarchy, such as selectNodes or attributes. We'll discuss these methods and properties while examining other object interfaces in the XML DOM.
The XMLDOMNodeList object is a collection of node objects. It is primarily used to iterate through the element nodes. This object implements the IXMLDOMNodeList interface. The IXMLDOMNodeList interface reflects the current state of the nodes in the document, so a change in the nodes will be immediately reflected in the object. The property and methods of IXMLDOMNodeList are as follows:
IXMLDOMNodeList Property
| Name | Description |
|---|---|
| length | Returns the number of nodes that are contained in the node list. |
IXMLDOMNodeList Methods
| Name | Description |
|---|---|
| item (index) | Returns the node located at position index in the node list. The first node is indexed as 0. |
| nextNode()* | Returns the next node object in the node list. If there are no more nodes, it returns null. |
| reset()* | Resets the pointer so that it points before the first node element. |
For an example of the IXMLDOMNodeList interface, you can add an attribute to the XML document and another command button to the frmDOMTest form. To do so, follow these steps:
- Open the XML document Books.xml and change the title element to the following:
<title language="English">Number, the Language of Science </title>
- Add another command button to the frmDOMTest form called cmdNodeList with the caption NodeList.
- Add the following code to the click event handler of the cmdNodeList button:
Private Sub cmdNodeList_Click() Dim objNodeList As IXMLDOMNodeList Dim objXMLDoc As DOMDocument Set objXMLDoc = New DOMDocument objXMLDoc.async = False objXMLDoc.Load ("c:\Books.xml") Set objNodeList = _ objXMLDoc.documentElement.firstChild.childNodes Debug.Print "The second item's basename is: " & _ objNodeList.Item(2).baseName Debug.Print "The number of nodes are: " & objNodeList.length Debug.Print "The first node xml is: " & vbCrLf & _ objNodeList.nextNode.xml Debug.Print "The second node xml is: " & _ objNodeList.nextNode.xml Debug.Print "The third node xml is: " & _ objNodeList.nextNode.xml objNodeList.Reset Debug.Print "After reset, the first node xml is: " & _ vbCrLf & objNodeList.nextNode.xml Dim intNodeCounter As Integer For intNodeCounter = 0 To objNodeList.length - 1 Debug.Print "The " & "xml of node" & _ Str(intNodeCounter + 1) & " is: " & vbCrLf & _ objNodeList.Item(intNodeCounter).xml Next Set objNodeList = Nothing Set objXMLDoc = Nothing End Sub
Notice that, once again, we first get a reference to the document object. Once we have this reference, we can get a reference to the IXMLDOMNodeList interface. Then we use the item, nextNode, and reset methods of the IXMLDOMNodeList interface to navigate the document. Last, we print all the nodes in the collection with a loop. When you run this updated application and click the NodeList button, the results are as follows:
The second item's basename is: price The number of nodes are: 4 The first node xml is: <title language="English">Number, the Language of Science</title> The second node xml is: <author>Danzig</author> The third node xml is: <price>5.95</price> After reset, the first node xml is: <title language="English">Number, the Language of Science</title> The xml of node 1 is: <title language="English">Number, the Language of Science</title> The xml of node 2 is: <author>Danzig</author> The xml of node 3 is: <price>5.95</price> The xml of node 4 is: <quantity>3</quantity> |
Notice that the attribute node was not included in the results. We will need to use the IXMLDOMNamedNodeMap interface to get a reference to attribute nodes.
The XMLDOMNamedNodeMap object implements the IXMLDOMNamedNodeMap interface. This interface is similar to the IXMLDOMNodeList interface except that it allows you to iterate through attributes and namespace nodes. The IXMLDOMNamedNodeMap interface has the same length property as the IXMLDOMNodeList interface. IXMLDOMNamedNodeMap also has the same item, nextNode, and reset methods as the IXMLDOMNodeList interface. The additional methods that are associated with the IXMLDOMNamedNodeMap are as follows:
Additional IXMLDOMNamedNodeMap Methods
| Name | Description |
|---|---|
| getNamedItem (name) | Retrieves the node object with the specified name. This method is usually used to retrieve an attribute from an element. |
| getQualifiedItem (baseName, namespace)* | Returns the node object with the specified baseName and namespace. |
| removeNamedItem (name) | Removes the node object that has the specified name from the named node map. This method is usually used to remove an attribute. |
| removeQualifiedItem (baseName, namespace)* | Removes the node object with the specified baseName and namespace. This method is usually used to remove attributes from the collection. |
| setNamedItem (newNode) | Inserts a new node into the collection. If a node with the same name as the newNode already exists, it's replaced. |
To illustrate how to use the methods and properties of IXMLDOMNamedNodeMap, add another command button to the frmDOMTest form with the name cmdNamedNodeMap and the caption NamedNodeMap. Add the following code to the click event handler of the cmdNamedNodeMap button:
Private Sub cmdNamedNodeMap_Click()
Dim objNamedNodeMap As IXMLDOMNamedNodeMap
Dim objXMLDoc As DOMDocument
Set objXMLDoc = New DOMDocument
objXMLDoc.async = False
objXMLDoc.Load ("c:\Books.xml")
Set objNamedNodeMap = objXMLDoc.documentElement.Attributes
Debug.Print _
"The root's first attribute node's basename is: " & _
objNamedNodeMap.Item(0).baseName
Debug.Print "The number of root's attribute nodes is: " & _
objNamedNodeMap.length
Debug.Print "The first node xml is: " & _
objNamedNodeMap.nextNode.xml
Set objNamedNodeMap = _
objXMLDoc.documentElement.firstChild.firstChild.Attributes
Debug.Print _
"The title element's attribute node's" & _
" basename is: " & objNamedNodeMap.Item(0).baseName
Debug.Print "The number of the title element's " & _
"attribute nodes is: " & objNamedNodeMap.length
Set objNamedNodeMap = Nothing
Set objXMLDoc = Nothing
End Sub
|
Once again, to move through the XML document you will begin by getting a reference to a document object. This time, you will use the attributes property of the document object to get a reference to the IXMLDOMNamedNodeMap interface. When you run this example and click the NamedNodeMap button, the results are as follows:
The root's first attribute node's basename is: northwind The number of the root's attribute nodes is: 1 The first node xml is:xmlns: northwind="www.northwindtraders.com/PO" The title element's attribute node's baseName is: language The number of the title element's attribute nodes is: 1 |
Thus, using the IXMLDOMDocument interface's attributes property and the IXMLDOMNamedNodeMap interface we are able to get information about the namespace and attribute nodes.
The XMLDOMDocumentType object implements IXMLDOMDocumentType interface. The doctype property of the IXMLDOMDocument interface identifies the document's IXMLDOMDocumentType interface. The IXMLDOMDocumentType interface gets information on the document type declaration in the XML document. This interface also extends the IXMLDOMNode interface, so it has all the properties and methods of the IXMLDOMNode interface. The IXMLDOMDocumentType interface also implements the following extended properties:
Additional IXMLDOMDocumentType Properties
| Name | Description |
|---|---|
| entities | Returns a node list containing references to the entity objects declared in the DTD |
| name | Returns the name of the document type for the document |
| notations | Returns a node list containing references to the notation objects in the DTD |
Now that the IXMLDOMDocumentType interface contains information associated with the DTD, let's create a DTD named Books.dtd for the document using the following text:
<!ELEMENT northwind:BOOKS (item)> <!ATTLIST northwind:BOOKS xmlns:northwind CDATA #FIXED "www.northwindtraders.com/PO"> <!ENTITY % ItemElements "(title, author, price, quantity)"> <!ENTITY copyright "©"> <!ELEMENT item %ItemElements;> <!ELEMENT title (#PCDATA)> <!ATTLIST title language CDATA #REQUIRED> <!ELEMENT author (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ELEMENT quantity (#PCDATA)> |
Notice that we declared a general entity called copyright in the above DTD, thus we need to reference this entity in the Books.xml document. We also need to add a line of code to the XML document so that it will reference the DTD:
<?xml version="1.0" ?> <!DOCTYPE northwind:BOOKS SYSTEM "c:\Books.dtd"> <northwind:BOOKS xmlns:northwind="www.northwindtraders.com/PO"> <item> <title language="English">Number, the Language of Science ©right; </title> <author>Danzig</author> <price>5.95</price> <quantity>3</quantity> </item> </northwind:BOOKS> |
NOTE
Remember that the DTD has no ability to resolve namespaces. Thus, you must declare the elements that use the namespace with the namespace prefix and define an attribute for the namespace. The XML document, though, can resolve the namespace information.
Now let's take a look at how to use the properties of the IXMLDOMDocumentType interface in our example application. First, add another command button to the frmDOMTest form with the name cmdDocumentType and the caption Document Type. Then add the following code to the click event of this button:
Private Sub cmdDocumentType_Click()
Dim objDocumentType As IXMLDOMDocumentType
Dim objXMLDoc As DOMDocument
Set objXMLDoc = New DOMDocument
objXMLDoc.async = False
objXMLDoc.Load ("c:\Books.xml")
Set objDocumentType = objXMLDoc.doctype
Debug.Print objDocumentType.Name
Debug.Print objDocumentType.xml
Debug.Print objDocumentType.entities.length
Debug.Print objDocumentType.entities.Item(0).xml
End Sub
|
When you run this example and click the DocumentType button, you'll see the following output:
northwind:BOOKS <!DOCTYPE northwind:BOOKS SYSTEM "c:\Books.dtd"> 1 <!ENTITY copyright "©"> |
Once again, you have created a reference to an IXMLDOMDocument interface. With this reference, you can use the doctype property to get a reference to an IXMLDOMDocumentType interface. Then you use the name and xml properties of the IXMLDOMDocumentType interface to get a node's name and its XML content. Notice that the parameter entity was not included in the entities collection.
The XMLDOMDocumentFragment object will be used to create fragments of documents that can be appended to another document. When the XMLDOMDocumentFragment object is inserted into a document object, the root node of the XMLDOMDocumentFragment is not inserted, only its children. Thus, XMLDOMDocumentFragment objects are useful for inserting child elements into a document.
The XMLDOMDocumentFragment object implements the IXMLDOMDocumentFragment interface. This interface inherits all the IXMLDOMNode interface's methods and properties, but it doesn't extend the interface, so this interface has no additional methods or properties of its own.
The XMLDOMElement object contains the elements in the document and is the most common node. The text nodes belonging to an element object contain the content of the element. If there is no text content, the XMLDOMText object will be null. This object implements the IXMLDOMElement interface. When working with the IXMLDOMElement interface, you must know beforehand what the names of the elements and attributes are that you want to retrieve and place them in the code. This is because the IXMLDOMElement interface sets and retrieves attributes and elements by their names.
In addition to the methods and properties of the IXMLDOMNode interface, the IXMLDOMElement interface has the following extended property and methods:
Extended IXMLDOMElement Property
| Name | Description |
|---|---|
| tagName | Sets or returns the name of the element |
Extended IXMLDOMElement Methods
| Name | Description |
|---|---|
| getAttribute (attributeName) | Returns the value of the attribute with the specified attributeName. |
| getAttributeNode (attributeName) | Returns the attribute node object with the specified attributeName. |
| getElementsByTagName (elementName) | Returns an XMLDOMNodeList object that contains all the descendant elements named elementName. |
| normalize() | Combines the adjacent text nodes into one unified text node. Normalizes all descendant text nodes of the element. |
| removeAttribute (attributeName) | Removes the value of the attribute named attributeName. |
| removeAttributeNode (attributeNode) | Removes the attribute node named attributeNode and returns the node. If there is a default value in the schema or DTD, a new attribute node will be created with the default value. |
| setAttribute (attributeName, newValue) | Sets the attribute node named attributeName to the value newValue. |
| setAttributeNode (attributeName) | Adds a new attribute node to the element. An existing attribute node by the same name will be replaced. |
You can get a reference to an IXMLDOMElement interface by using the selectNodes method and XSL. You will now create an example to select a single element node. To do so, follow these steps:
- Add another command button to the frmDOMTest form with the name cmdElement and the caption Element.
- Insert the following code into the click event handler of this button:
Private Sub cmdElement_Click() Dim objXMLDoc As DOMDocument Dim objElement As IXMLDOMElement Set objXMLDoc = New DOMDocument objXMLDoc.async = False objXMLDoc.Load ("c:\Books.xml") Set objElement = objXMLDoc.selectNodes("//item/*").Item(1) Debug.Print objElement.xml Set objElement = Nothing Set objXMLDoc = Nothing End Sub
This example application selects the second child node of the item element. Then it retrieves the entire XML content of this node by using the xml property. When you run this example and click the Element button, the result is as follows:
<author>Danzig</author> |
You can get a reference to the XMLDOMAttribute, XMLDOMEntity, XMLDOMEntityReference, XMLDOMNotation, XMLDOMCharacterData, XMLDOMText, XMLDOMCDATASection, XMLDOMComment, and XMLDOMProcessingInstruction by using XSL. You can get references to these node objects just as we used the XSL statement "//item/*" to get references to the node item in the previous application. (We will discuss the XSL syntax in detail in Chapter 12. ) So in the following sections, we'll examine these interfaces without demonstrating how to use them in the applications.
The XMLDOMAttribute object represents an attribute node of the XMLDOMElement object. This object implements the IXMLDOMAttribute interface. In addition to the properties and methods it inherits from the IXMLDOMNode interface, the IXMLDOMAttribute interface has the following additional properties:
Extended IXMLDOMAttribute Properties
| Name | Description |
|---|---|
| name | Sets or returns the name of the attribute |
| value | Sets or returns the value of the attribute |
NOTE
The W3C specification lists this object as the attr object, instead of the XMLDOMAttribute object.
The XMLDOMEntity object represents a parsed or unparsed entity declared in a DTD. The XMLDOMEntity object is not the entity declaration. This object implements the IXMLDOMEntity interface. The properties of this interface are read-only. Like most of the other DOM interfaces, this interface inherits the IXMLDOMNode interface too. In addition to the IXMLDOMNode properties and methods, the IXMLDOMEntity object extends the IXMLDOMNNode object with the following properties:
Extended IXMLDOMEntity Properties
| Name | Description |
|---|---|
| publicID | Returns the value of the PUBLIC identifier for the entity node |
| systemID | Returns the value of the SYSTEM identifier for the entity node |
| notationName | Returns the notation name |
The XMLDOMEntityReference object represents an entity reference node contained in the XML document. Remember that an XML processor doesn't expand the entities until they are needed. Thus, if the XML processor doesn't expand the entities, there will be no XMLDOMEntityReference objects. The replacement text will be located in the text property. The IXMLDOMEntityReference interface implemented by the XMLDOMEntityReference object inherits all the methods and properties of, but does not extend, the IXMLDOMNode interface
The XMLDOMNotation object represents a notation declared in the DTD with the declaration <!NOTATION>. The XMLDOMNotation object implements the IXMLDOMNotation interface that inherits all the methods and properties of the IXMLDOMNode interface and extends the IXMLDOMNode interface with the following properties:
Additional IXMLDOMNotation Properties
| Name | Description |
|---|---|
| publicID | Returns the value of the PUBLIC identifier for the notation node |
| systemID | Returns the value of the SYSTEM identifier for the notation node |
The XMLDOMCharacterData object makes it easier to work with the text content in an XML document. The IXMLDOMCharacterData interface implemented by the XMLDOMCharacterData object also inherits the IXMLDOMNode interface, so it includes all the properties and methods of the IXMLDOMNode interface. Moreover, it extends the IXMLDOMNode interface with the following properties and methods:
Extended IXMLDOMCharacterData Properties
| Name | Description |
|---|---|
| data | Contains the node's data. The actual data will depend on the type of node. |
| length | Returns the number of characters in the data string. |
Extended IXMLDOMCharacterData Methods
| Name | Description |
|---|---|
| appendData (text) | Appends the text argument onto the existing data string |
| deleteData (charOffset, numChars) | Deletes numChars characters off the data string starting at charOffset |
| insertData (charOffset, text) | Inserts the supplied text into the data string at the charOffset |
| replaceData (charOffset, numChars, text) | Replaces numChars characters with the supplied text starting at charOffset |
| substringData (charOffset, numChars) | Returns the numChars characters as a string, starting at charOffset, in the data string |
The XMLDOMText object represents the text node of an element or an attribute. You can use the XMLDOMText object to build text nodes and append them into an XML document. The IXMLDOMText interface implemented by the XMLDOMText object inherits the IXMLDOMCharacterData interface and extends it with the following method:
Extended IXMLDOMText Method
| Name | Description |
|---|---|
| splitText (charOffset) | Splits the node into two nodes at the specified character offset and then inserts the new node into the XML document immediately following the node |
An XMLDOMCDATASection object is used for sections of text that are not to be interpreted by the processor as markup. The XMLDOMCDATASection object implements the IXMLDOMCDATASection interface. This interface inherits the IXMLDOMText interface and has the same methods and properties as the IXMLDOMText interface.
The XMLDOMComment object contains comments that are in the XML document. The IXMLDOMComment interface implemented by this object inherits the IXMLDOMCharacterData interface and also possesses the same methods and properties as IXMLDOMCharacterData. The IXMLDOMComment interface does not extend the IXMLDOMCharacterData interface.
The XMLDOMProcessingInstruction object contains the processing instructions in the document between the <? tag and the ?> tag. The content enclosed in these two tags is divided into the target and data content. The IXMLDOMProcessingInstruction interface implemented by the XMLDOMProcessingInstruction object inherits the IXMLDOMNode interface and has the same methods as the IXMLDOMNode interface. It extends the IXMLDOMNode interface with the following properties:
Extended IXMLDOMProcessingInstruction Properties
| Name | Description |
|---|---|
| data | Sets or returns the content of the processing instruction, which doesn't contain the target |
| target | Sets or returns the target application to which the processing instruction is directed |
Because different applications that support XML can support different features of XML, the W3C included the XMLDOMImplementation object, which can be used to determine whether certain features are supported in a particular application. The XMLDOMImplementation object implements the IXMLDOMImplementation interface. This interface has one method called hasFeature that returns true if the specified feature is implemented by the specified version of the XML DOM implementation. To see how this object works, add another command button to the frmDOMTest form with the name cmdImplementation and the caption Implementation. Add the following code to the click event of this button:
Private Sub cmdImplementation _Click()
Dim objImplementation As IXMLDOMImplementation
Dim objXMLDoc As DOMDocument
Set objXMLDoc = New DOMDocument
objXMLDoc.async = False
objXMLDoc.Load ("c:\Books.xml")
Set objImplementation = objXMLDoc.implementation
'Currently accepted values for feature: XML, DOM, and MS-DOM
Debug.Print "MS-DOM: " & _
objImplementation.hasFeature("MS-DOM", "1.0")
Debug.Print "XML: " & _
objImplementation.hasFeature("XML", "1.0")
Debug.Print "DOM: " & _
objImplementation.hasFeature("DOM", "1.0")
End Sub
|
If you have Internet Explorer 5 installed on your computer, running this application and clicking the Implementation button will give you the following results:
MS-DOM: True XML: True DOM: True |
HasFeature returning true shows that Internet Explorer 5 supports XML, DOM, and the MS-DOM.
The XMLDOMParseError object is an extension to the W3C specification. It can be used to get detailed information on the last error that occurred while either loading or parsing a document. The XMLDOMParseError object implements the IXMLDOMParseError interface that has the following properties:
IXMLDOMParseError Properties
| Name | Description |
|---|---|
| errorCode | Returns the error number or error code as a decimal integer. |
| filepos | Returns the absolute character position in the document where the error occurred. |
| line | Returns the number of the line where the error occurred. |
| linepos | Returns the absolute character position in the line where the error occurred. |
| reason | Returns a description of the source and reason for the error. If the error is in a schema or DTD, it can include the URL for the DTD or schema and the node in the schema or DTD where the error occurred. |
| srcText | Returns the full text of the line that contains the error. If the error cannot be assigned to a specific line, an empty line is returned. |
| url | Returns the URL for the most recent XML document that contained an error. |
To see how the IXMLDOMParseError interface is used, we will make a change to the first line of code in the Books.dtd: <!ELEMENT northwind:BOOKS (item)> by removing the northwind: from the line, as shown here:
<!ELEMENT BOOKS (item )> |
Add another command button to the frmDOCTest form with the name cmdParseError and the caption ParseError. In the click event handler of this button, place the following code:
Private Sub cmdParseError _Click()
Dim objXMLDoc As DOMDocument
Dim objXMLParseError As IXMLDOMParseError
On Error GoTo cmdParseErrorError
Set objXMLDoc = New DOMDocument
objXMLDoc.async = False
objXMLDoc.Load ("c:\Books.xml")
'Check whether there was an error parsing the file using the
'parseError object.
If objXMLDoc.parseError.errorCode <> 0 Then
'If there was an error, raise it to jump into error trap.
Err.Raise objXMLDoc.parseError.errorCode
End If
Exit Sub
'Error Trap
cmdParseErrorError:
Set objXMLParseError = objXMLDoc.parseError
With objXMLParseError
'Because of the With objXMLParseError, .errorCode is the
'same as objXMLParseError.errorCode.
'First check whether the error was caused by a parse error.
If .errorCode <> 0 Then
Debug.Print "The following error occurred:" & vbCrLf & _
"error code: " & .errorCode & vbCrLf & _
"error file position: " & .filepos & vbCrLf & _
"error line: " & .Line & vbCrLf & _
"error line position: " & .linepos & vbCrLf & _
"error reason: " & vbCrLf & .reason & vbCrLf & _
"error source text: " & vbCrLf & .srcText & _
" Test:cmdParseError"
Else
'If the error was not caused by a parse error, use
'regular Visual Basic error object.
MsgBox "The following error has occurred:" & vbCrLf & _
"Error Description: " & Err.Description & _
vbCrLf & _
"Error Source: " & vbCrLf & Err.Source & _
" Test:cmdParseError" & vbCrLf & _
"Error Number: " & Err.Number
End If
End With
Set objXMLParseError = Nothing
Set objXMLDoc = Nothing
End Sub
|
Before you actually run this code, take a look at it to see what an error handler in production code should look like. A parse error does not raise its error. After the parse error occurs, the Visual Basic error number (Err.Number) is still 0. Thus, you must use the ParseError object to check for an error after you load an XML document. If there is a parse error, you can raise an error, as was done above, to bring you into the error trap.
The error trap provides a message if the error occurred in parsing the file. If the error was caused for some other reason, the standard Visual Basic Err error object is used. Also notice that the name of the application and the method are included in the source, making it easier to find and fix bugs.
Now you can run the updated application and click the ParseError button. With the change in the DTD, you will receive the following message:
The following error occurred: error code: -1072898035 error file position: 137 error line: 3 error line position: 64 error reason: The element 'northwind:BOOKS' is used but not declared in the DTD/Schema. error source text: <northwind:BOOKS xmlns:northwind="www.northwindtraders.com/PO"> Test:cmdParseError error source text: <northwind:BOOKS xmlns:northwind="www.northwindtraders.com/PO"> |
In this case, because the DTD has no awareness of namespaces, the namespace qualified northwind:BOOKS in the XML document no longer matches the DTD declaration.
To create a different error, change the DTD back to its original form by adding back the northwind:. Remove #FIXED "www. northwindtraders.com/PO" from the second line in the DTD, and the second line will look as follows:
<!ATTLIST ":BOOKS xmlns: "CDATA> |
Now run the application and click the ParseError button; the error message will look as follows:
The following error occurred: error code: -1072896766 error file position: 86 error line: 2 error line position: 51 error reason: A string literal was expected, but no opening quote character was found. error source text: <!ATTLIST northwind:BOOKS xmlns:northwind CDATA> Test:cmdParseError error source text: <!ATTLIST northwind:BOOKS xmlns:northwind CDATA> |
Notice that the error source text is now the information from the DTD. The reason might not be that obvious, but by looking at the error reason you will see that you need to include #REQUIRED, #FIXED, or #IMPLIED in the DTD. You must use #FIXED because this is a namespace attribute.
The XTLRuntime object works with XSL style sheets. It implements the IXTLRuntime interface that has nine methods: absoluteChildNumber, ancestorChildNumber, childNumber, depth, formatDate, formatIndex, formatNumber, formatTime, and uniqueID. We will cover XSL style sheets and these methods in Chapter 12.
The XMLHTTPRequest object, which is an extension of the W3C specification, can be used to send and receive HTTP messages to and from a Web server. Once a message is received, it can be parsed by XML DOM objects. You could use the XMLHTTPRequest object to create applications that build and send SOAP messages to the server. This object implements the IXMLHTTPRequest interface, which has the following properties and methods:
IXMLHTTPRequest Properties
| Name | Description |
|---|---|
| readyState | Indicates the current state of the document being loaded. The value changes as the XML document loads. |
| responseBody | Returns the response as an array of unsigned bytes. |
| responseStream | Returns the response object as an IStream object. |
| responseText | Returns the response object as a text string. |
| responseXML | Returns the response as an XML document. When this property is used, validation is turned off to prevent the parser from attempting to download a linked DTD or schema. |
| status | Returns a status code as a long integer. |
| statusText | Returns the status as a string. |
IXMLHTTPRequest Methods
| Name | Description |
|---|---|
| abort()* | Cancels the current HTTP request |
| getAllResponseHeaders()* | Returns all the HTTP headers as name value pairs separated by carriage return-linefeeds |
| getResponseHeader (headerName)* | Gets the response header with the name headerName |
| open (method, url, async, userID, password)* | Initializes a request and specifies the HTTP method, the URL, and if the response is asynchronous, the user information |
| send()* | Sends an HTTP request to the server and waits to receive a response |
| setRequestHeader (headerName, value) | Sets HTTP headers that are sent to the server |
This chapter has described a complete set of objects that can allow you to manipulate XML information and send HTTP streams to and from a Web server. It's time we look at a more complete example of using some of these objects, including the XMLHTTPRequest object. To do this, we will write the code to create a SOAP client and server application.
The SOAP application you are about to create will use Visual Basic on the client side and Active Server Pages (ASP) on the server side. The XML DOM will be used on both the client and the server.
Instead of loading XML data from a file or a string, this application will build an XML file using the XML DOM objects and show you how to write code to build XML documents dynamically.
In a real application, you could build the XML document using a Visual Basic application that processes SOAP requests or use JScript or VBScript in a browser application with the values that the user has inputted into the browser form. For this example, we will provide values for the XML elements.
We will start by creating a new Visual Basic EXE project. For this example, you will need a copy of the XML parser, which at the time of this printing is version 3. Thus, you should add a reference to the Microsoft XML 3.0 by choosing References from the Project menu, and then choosing Microsoft XML version 3 from the References dialog box.
NOTE
This book will use both the 3.0 and 2.6 versions of the XML parser. When you are working with these examples, you can use the most current version by changing the declarations of the DOMDocument objects.
Add a button to the frmDOMTest form with the name cmdSoap and the caption Soap. Enter the following code into the click event handler of this button:
Dim objXMLDoc As DOMDocument30
Dim objHTTPRequest As XMLHTTP30
Dim objXMLDocResponse As DOMDocument30
Set objHTTPRequest = New XMLHTTP30
Set objXMLDoc = New DOMDocument
Set objXMLDocResponse = New DOMDocument30
Set objXMLDocNode = New DOMDocument30
Dim lngResponse As Long
Dim lngOrderID As Long
Dim strResponse As String
objXMLDoc.async = False
'We will begin by using the loadXML string to load in the root
'node.
objXMLDoc.loadXML _
"<SOAP-ENV:Envelope xmlns:SOAP-ENV = " & _
"'http://schemas.xmlsoap.org/soap/envelope'" & _
" xmlns:xsi='http://www.w3.org/1999/XMLSchema/instance' " & _
" xsi:schemaLocation=" & _
"'http://www.northwindtraders.com/schemas/NPOSchema.xsd'>" & _
"<SOAP-ENV:Body xsi:type='northwindBody'><UpdatePO>" & _
"<OrderID>" & "</OrderID>" & _
"<return>" & strReturn & _
"</return></UpdatePO></SOAP-ENV:Body>" & _
"</SOAP-ENV:Envelope>"
'The createNode method returns a node object.
'If the element has a namespace prefix, it must be included.
'The appendChild method will take the node object created with
'createNode and add it to XML document node collection.
objXMLDoc.documentElement.appendChild _
objXMLDoc.createNode(NODE_ELEMENT, "SOAP-ENV:Body", _
"'http://schemas.xmlsoap.org/soap'")
'For an attribute, you must use the attributes property.
objXMLDoc.documentElement.firstChild.Attributes.setNamedItem _
objXMLDoc.createNode(NODE_ATTRIBUTE, "xsi:type", _
"xmlns:xsi='http://www.w3.org/1999/XMLSchema/instance'")
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body").appendChild _
objXMLDoc.createNode(NODE_ELEMENT, "UpdatePO", "")
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
appendChild objXMLDoc.createNode(NODE_ELEMENT, "OrderID", "")
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
appendChild objXMLDoc.createNode _
(NODE_ELEMENT, "CustomerNumber", "")
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
appendChild objXMLDoc.createNode(NODE_ELEMENT, "Item", "")
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
appendChild objXMLDoc.createNode(NODE_ELEMENT, "Quantity", "")
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO"). _
appendChild objXMLDoc.createNode(NODE_ELEMENT, "return", "")
'We must now set the values for each node.
'We will use XSL in selectSingleNode to get the node.
'For the attribute, we must use getNamedItem to get the attribute.
objXMLDoc.selectSingleNode("SOAP-ENV:Envelope/SOAP-ENV:Body"). _
Attributes.getNamedItem("xsi:type").nodeValue = "NorthwindBody"
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/OrderID").Text = "0"
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/CustomerNumber"). _
Text = "999"
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Item").Text = "89"
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/return").Text = "0"
objXMLDoc.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Quantity"). _
Text = "35"
'Initialize HTTP request with Post.
objHTTPRequest.open "POST", "http://localhost/XMLSample/SOAP.asp"
'Set the SOAP headers.
objHTTPRequest.setRequestHeader "POST", "/Northwind.Order HTTP/1.1"
objHTTPRequest.setRequestHeader "Host", "www.northwindtraders.com"
objHTTPRequest.setRequestHeader "Content-Type", "text/xml"
objHTTPRequest.setRequestHeader "content-length", _
Len(objXMLDoc.xml)
objHTTPRequest.setRequestHeader _
"SOAPAction", "urn: northwindtraders.com:PO#UpdatePO"
'Send the message.
objHTTPRequest.send objXMLDoc.xml
'Set the response document object equal to the responseXML object.
Set objXMLDocResponse = objHTTPRequest.responseXML
'Wait to get result.
Dim lLoopNum As Long
Do While objXMLDocResponse.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/return") Is Nothing _
And lLoopNum < 10000
DoEvents
lLoopNum = lLoopNum + 1
Loop
'Get the return values.
strResponse = objXMLDocResponse.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/return").Text
If strResponse = "" Then
'Raise an error here.
Else
MsgBox "Response = " & strResponse
End If
lngOrderID = CLng(objXMLDocResponse.selectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/OrderID").Text)
Set objXMLDocResponse = Nothing
Set objXMLDoc = Nothing
Set objHTTPRequest = Nothing
|
To be able to run the application, you need to create an object named objOrderPO that contains the UpdatePO method. Since we will not actually create this object, we will comment out the code where the method will be created and create dummy variables so you can run the example. You also need to create an ASP page named SOAP.asp and put it on the local server in a folder called XMLSample under the default Web site. Now let's take a look at how to create an ASP page.
The ASP page uses two document objects, one called objXMLDocRequest, which holds the XML document sent from the server, and the other called objXMLDocResponse, which contains the XML document that is returned to the server. The names of the object and the method that need to be called are retrieved from the header first. Next you retrieve the XML document from the request object, retrieve the parameter values located in the XML body, and finally create the object and call the method. Once the method has been called, the return XML string is built and placed in the response object.
Create an ASP page and add the following code:
<%@ Language=VBScript %>
<SCRIPT LANGUAGE=vbscript RUNAT=Server>
Dim objXMLDocRequest
Dim objXMLDocResponse
Dim result, strObject, strMethod
Dim strCustomerNumber, strItem, strReturn
Dim strOrderID, strQuantity
Set objXMLDocRequest= Server.CreateObject ("Microsoft.XMLDOM")
Set objXMLDocResponse= Server.CreateObject ("Microsoft.XMLDOM")
'You must set the content type so that the returning document will
'be identified as XML.
Response.ContentType = "text/xml"
'Load the posted XML document.
strObject= Request.ServerVariables.Item ("HTTP_POST")
'Remove /
strObject= Right(strObject, len(strObject)-1)
'Remove HTTP...
strObject= Left(strObject, instr(1, strObject, " ") )
strMethod= Request.ServerVariables.Item ("HTTP_SOAPAction")
'Strip off URL.
strMethod=Right(strMethod, len(strMethod)-instr(1, strMethod,"#"))
'Use the load method to get the XML sent from the client out of the
'request object.
objXMLDocRequest.load Request
'Now that you have the XML data, use the values in the XML
'document to set the local variables.
strCustomerNumber=objXMLDocRequest.SelectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/CustomerNumber").Text
strItem = objXMLDocRequest.SelectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Item").Text
strQuantity = objXMLDocRequest.SelectSingleNode _
("SOAP-ENV:Envelope/SOAP-ENV:Body/UpdatePO/Quantity").Text
'Using the name of the object passed in the header, we will
'instantiate the object.
'Because we do not actually have an object called objOrderPO, we
'will comment out the next line of code.
'Set objOrderPO = _
' server.CreateObject (Request.ServerVariables.Item(strObject))
'Call the correct method passing in the parameters.
Select Case strMethod
Case "UpdatePO"
'Because we do not actually have an object called objOrderPO, we
'will comment out the next two lines of code. We are also adding
'dummy values for the strReturn and strOrderID variables.
'strReturn=objOrderPO.UpdatePO _
'(strCustomerName, strItem, strQuantity, strOrderID)
strReturn="0"
strOrderID="100"
Case "DeletePO"
strReturn=objOrderPO.DeletePO _
(strCustomerName, strItem, strQuantity, strOrderID)
End Select
'Create XML that is going back to the client using a string.
objXMLDocResponse.LoadXML _
"<SOAP-ENV:Envelope xmlns:SOAP-ENV =" & _
"'http://schemas.xmlsoap.org/soap/envelope'" & _
" xmlns:xsi='http://www.w3.org/1999/XMLSchema/instance' " & _
" xsi:schemaLocation=" & _
"'http://www.northwindtraders.com/schemas/NPOSchema.xsd'>" & _
"<SOAP-ENV:Body xsi:type='northwindBody'><UpdatePO>" & _
"<OrderID>" & strOrderID & "</OrderID>" & _
"<return>" & strReturn & _
"</return></UpdatePO></SOAP-ENV:Body>" & _
"</SOAP-ENV:Envelope>"
'Return the XML.
Response.Write objXMLDocResponse.xml
</SCRIPT>
|
In this example, you can see how tightly bound the client-side code is to the object that is being called. The client-side object has to build an XML document that contains the correct parameters and also has to create a header with the right object and method names.
The XML parser version 2.6 and version 3.0 extend the older version that came with Internet Explorer 5. The XML parser version 2.6 is a separate DLL (Msxml2.dll) that can be installed in addition to the original XML parser. Various options are available for running the two DLLs together, but those options are beyond the scope of this book. You can check the XML SDK 2.5 (which can be viewed on Microsoft's Web site) for more information about the DLLs. Version 3.0 of the parser is installed as a new DLL (Msxml3.dll) with new version-dependent CLSIDs and ProgIDs to protect those applications that use Msxml.dll or Msxml2.dll and allow you to choose the version of the parser to use in your code.
Version 2.6 comes with five additional XML document objects: XMLDOMDocument2, XMLDOMSchemaCache, XMLDOMSelection, XMLDOMXSLProcessor, and XMLDOMXSLTemplate. We will discuss the XMLDOMXSLTemplate and XMLDOMXSLProcessor objects in Chapter 12 when we discuss XSL. Version 3.0 doesn't add any new features to version 2.6. Thus, we'll have a detailed discussion about the XMLDOMDocument2, XMLDOMSchemaCache, and XMLDOMSelection objects in the following section.
The XMLDOMDocument2 object implements the IXMLDOMDocument2 interface. This interface is an extension of the IXMLDOMDocument interface that supports schema caching and validation. The IXMLDOMDocument2 interface inherits all the original properties and methods of the IXMLDOMDocument interface and adds the following new properties:
Extended IXMLDOMDocument2 Properties
| Name | Description |
|---|---|
| namespaces | Returns a list of all of the namespaces in the document as an XMLDOMSchemaCollection(schemaCache object) |
| schemas | Locates all the schema documents using the XMLDOMSchemaCollection (schemaCache object) |
The XMLSchemaCache object which implements the IXMLDOMSchemaCollection interface contains information about the schemas and namespaces used by an XML document. This interface has the following property and methods:
IXMLDOMSchemaCollection Property
| Name | Description |
|---|---|
| length | Returns the number of namespaces that are currently in the collection |
IXMLDOMSchemaCollection Methods
| Name | Description |
|---|---|
| add (namespaceURI, schema) | Adds a new schema to the schema collection. The specified schema is associated with the given namespace URI. |
| addCollection (XMLDOMSchemaCollection) | Adds all the schemas from another collection into the current collection. |
| get (namespaceURI) | Returns a read-only DOM node containing the <Schema> element. |
| namespaceURI (index) | Returns the namespace for the specified index. |
| remove (namespaceURI) | Removes the specified namespace from the collection. |
Now that we've examined the properties and methods in the IXMLDOMDocument2 interface and the IXMLDOMSchemaCollection interface, it's time to see how they are used in the application. Create a new Visual Basic Standard EXE project, and name the default form frmTestDOM2. In the form frmTestDOM2, place a command button with the name cmdSchemas and the caption Schemas. Place the following code in the click event handler of this button:
Private Sub cmdSchemas_Click()
Dim objXMLDoc As DOMDocument26
Dim objXMLSchemas As XMLSchemaCache
Dim lngSchemaCounter As Long
Set objXMLDoc = New DOMDocument
objXMLDoc.async = False
objXMLDoc.Load ("c:\Books.xml")
Set objXMLSchemas = objXMLDoc.namespaces
For lngSchemaCounter = 0 To objXMLSchemas.length - 1
Debug.Print "URL: " & _
objXMLSchemas.namespaceURI(lngSchemaCounter)
Debug.Print "Type: " & _
objXMLDoc.selectSingleNode("//author").dataType
Next
Set objXMLSchemas = Nothing
Set objXMLDoc = Nothing
End Sub
|
This application loads the Books.xml document that contains a reference to the Books.dtd. If you run this program and click the Schemas button when the XML document is referencing the DTD, you will get the following result:
URL: www.northwindtraders.com/PO Type: |
Looking back at the original XML document, you have the following two lines of code:
<!DOCTYPE northwind:BOOKS SYSTEM "c:\Books.dtd"> <northwind:BOOKS xmlns: northwind ="www.northwind.com/PO"> |
You can see that the !DOCTYPE declaration defined the location of the DTD. Because XML documents are aware of namespaces, the parser recognized that the prefix northwind was a namespace prefix for the BOOKS element. In the next line of code, the parser finds the declaration that associates a namespace with that name and with that declaration. There is no data type information in the result above because this is a DTD.
We will now use a BizTalk schema instead of a DTD. Create the following schema file called Books.xsd:
<Schema name="BOOKS" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes"> <ElementType name="BOOKS" content="eltOnly" model="closed"> <element type="item"/> </ElementType> <ElementType name=" item" content="eltOnly" model="closed"> <element type="title"/> <element type="author"/> <element type="price"/> <element type="quantity"/> </ElementType> <ElementType name="title" content="mixed" model="closed"> <AttributeType name="language" dt:type="string"/> <attribute type="language"/> </ElementType> <ElementType name="author" content="textOnly" model="closed" dt:type="string"/> <ElementType name="price" content="textOnly" model="closed" dt:type="string"/> <ElementType name="quantity" content="textOnly" model="closed" dt:type="string"/> </Schema> |
Create a new XML document called Books2.xml and add the following code:
<?xml version="1.0" ?> <northwind:BOOKS xmlns:northwind="x-schema:c:\Books.xsd"> <northwind:item> <northwind:title language="English">Number, the Language of Science</northwind:title> <northwind:author>Danzig</northwind:author> <northwind:price>5.95</northwind:price> <northwind:quantity>3</northwind:quantity> </northwind:item> </northwind:BOOKS> |
Notice that to get a reference to the schema we must use x-schema in the namespace declaration. The x-schema syntax is used by Internet Explorer 5 to identify where the schema is. The namespace prefix had to be added to all the elements in order for this code to work. While that should not have been necessary, errors would result if the namespace prefix was not added. This is a reminder that the way things are implemented might not always be what you expect. Finally, change the Visual Basic code so that it references Books2.xml and the namespace prefix is included in the XSL statement:
Private Sub cmdSchemas_Click()
Dim objXMLDoc As DOMDocument26
Dim objXMLSchemas As XMLSchemaCache
Dim lngSchemaCounter As Long
Set objXMLDoc = New DOMDocument
objXMLDoc.async = False
objXMLDoc.Load ("c:\Books2.xml")
Set objXMLSchemas = objXMLDoc.namespaces
For lngSchemaCounter = 0 To objXMLSchemas.length - 1
Debug.Print "URL: " & _
objXMLSchemas.namespaceURI(lngSchemaCounter)
Debug.Print "Type: " & objXMLDoc.selectSingleNode & _
("//northwind:author").dataType
Next
End Sub
|
After you make the changes, running the program and clicking the Schemas button will result in the following output:
URL: x-schema:c:\Books.xsd Type: string |
As you can see, we get the data type information this time because the schema contains the data type information.
The XMLDOMSelection object represents a list of nodes that match an XSL pattern or an XPath expression. The XMLDOMSelection object can be created by using the selectNodes method of the IXMLDOMDocument2 interface that is included in version 2.6 and later of Microsoft XML parser. This object implements the IXMLDOMSelection interface, which inherits from the IXMLDOMNodeList interface. In Visual Basic, the XMLDOMSelection object can be used as follows:
Dim objXMLDoc As DOMDocument26
Dim objXMLSelection As IXMLDOMSelection
Set objXMLDoc = New DOMDocument26
objXMLDoc.async = False
objXMLDoc.Load ("C:\Books.xml")
Set objXMLSelection = objXMLDoc.selectNodes("//item [quantity=3]")
Debug.Print objXMLSelection.expr
|
In this example, we select the item element that has an attribute with a value of 3. The expr property of IXMLDOMSelection is used to retrieve the XPath expression string. In this case, "//item [quantity=3]" is returned.
In this chapter, you reviewed the majority of the XML DOM objects. These objects allow you to manipulate an XML document in virtually any way that might be required by an application. These objects provide a powerful set of tools that allow you to begin building complete XML applications. You can also use the DOM objects to both send messages to and receive messages from a Web server, which allows you to create SOAP messages as shown in the example in this chapter. In the next chapter, we will look at XSL and learn how the DOM can be used with it.
discuss this topic to forum
