Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

An MFC Wrapper for MSXML


February 2001/An MFC Wrapper for MSXML


Is XML (eXtensible Markup Language) for real? In a nutshell, yes. It seems that every industry is hopping on the bandwagon. Many companies realize the need to communicate electronically, and most of the past methods have not lived up to expectation. With the adoption of the Internet as the communications medium of choice and the concerted efforts by the W3C and companies around the world to standardize XML tools, XML is quickly becoming the technology of choice to facilitate communications between systems. And, if you think that XML is simply a markup language for data encapsulation, think again. There are many XML-based specifications that extend its reach outside of generic text markup facilities. For example (and this list is by no means exhaustive), XLink provides a mechanism for linking other resources to an XML document, XPath is a specification for addressing parts of an XML document, and XSLT can be used to transform documents. Additionally, specifications like BizTalk, SOAP (Simple Object Access Protocol), and cXML (Commerce XML) are being adopted to standardize the format of information transfer across the Internet.

This article briefly describes XML and an MFC wrapper that provides a simplified interface to Microsoft’s XML DLL. This wrapper makes it easy to write MFC code that loads an XML document into memory as a tree structure, modifies the XML tree, and saves it back to disk as XML again.

XML Background

XML is a simple mechanism for delimiting text data. It is used to markup a document’s content using tags as the delimiter. Additionally, by using those tags, we can define documents of arbitrary structure. Except for obeying the syntax rules, XML places no restrictions on what the document can contain. Let’s look at a document example:

<vehicle type="car" year="1999">
    <manufacturer>Ford</manufacturer>
    <model>Mustang</model>
    <price/>
</vehicle>

Elements are the basic building blocks of an XML document. Each element has a name that describes the organization of the data in a hierarchical fashion. The tags vehicle, manufacturer, price, and model are the names of the elements of this document. Attributes are used to modify an element. The tags type and year are attributes of the element vehicle, whereas, "car" and "1999" are the values of those attributes. The content of an element is the text between the tags. Ford and Mustang are the text associated with the elements manufacturer and model. The above example is not exhaustive in covering the possible content of an XML document, but it does illustrate the primary components of one.

As previously stated, there are syntax rules. Except for the case of the “empty-element” tag, all elements must have a start and an end tag. Start tags consist of an element name and its attributes enclosed within pair of angle brackets. End tags consist of just the element name preceded by a forward slash. In the above example, <model> would be the start tag and </model> would be the end tag for the element model. <price/> would be considered an empty-element tag because it contains no additional elements or other content. Placing the forward slash at the end of the start tag is OK in this case as it denotes the price element in an abbreviated form.

Elements can be nested (as they are in the above example), but they cannot overlap. The following is an example of an illegal overlap.

<manufacturer>Ford<model></manufacturer>Mustang</model>

The model start tag appears before the manufacturer end tag, and the manufacturer end tag resides between the model start and end tags.

Attributes modify the element that they are associated with. They are constructed as name-value pairs. Values of attributes must be enclosed in either single or double quotes. Either of the following is legal:

<vehicle type="car" year="1999"></vehicle>
<vehicle type='car' year='1999'></vehicle>

A document is considered to be “well-formed” XML if it conforms to the syntax of the XML 1.0 Recommendation. All of the above-identified syntax rules are part of this specification. While there are many other rules contained in this specification, for brevity purposes, I will pass on discussing them. What I have covered so far is sufficient for now. For those interested in a very good book on XML and its related technologies, check out Professional XML published by WROX Press. This book is very beefy and sometimes a little wordy, but it does an excellent job of covering the many aspects of XML.

XML DOM

There are various ways to access the content of an XML document. You could write all the code necessary to handle the XML 1.0 Recommendation. But, this would be time consuming, prone to bugs, and, as you will soon see, unnecessary. You could simply parse the document with the tag structure hard-coded into strings and variables. But, this would be too restrictive and would not take advantage of reusing existing code for different document structures. Or, you could use one of the two current “standard” implementations: SAX (Simple API for XML) or DOM (Document Object Model).

SAX is an event-driven interface that informs an application of the document contents by firing off a stream of events as it parses the document. The main advantage of this interface is that SAX does not load the whole document into memory while it is parsing it. This can be attractive if the document you are parsing is very large. On the down side though, it does not allow for random searching, since SAX does not keep an element in memory after it fires an application event.

In contrast to SAX, the DOM takes the complete XML document and transforms it into a node tree that can be accessed at will by the application. The node tree closely mirrors the document itself. And, since the complete document is in memory, random searches are allowed. Another feature of the DOM is that it guarantees that the document will be “well-formed” when saving it. This is possible because the details of the syntax are hidden from the application developer. The down side is that a large amount of memory can be consumed for large documents. In many cases though, this should not be a problem. Many XML specifications are for business transactions, passing configuration information, and executing applications on remote machines. The XML document size for these types of communications is usually relatively small.

The DOM is designed to be language- and platform-independent. The W3C DOM specification defines the interfaces for the objects comprising the DOM, but it does not specify anything to do with the implementation details. Enter the third-party library. Enter Microsoft. Their implementation of the DOM (as well as other XML specification implementations) is contained in msxml.dll and is installed as part of Internet Explorer 5 and later. (Note that Microsoft is continually updating their implementation of MSXML because of the dynamics of the W3C specifications and the new features that they are adding. At the time of this writing, Microsoft was working on version 3 of MSXML. All of the features covered in this article are basic and have been part of the DLL for a while. You should check which version of the DLL you have on your system prior to attempting to use some of the more advanced XML features or SAX.)

XML DOM (MFC) Class

Microsoft’s implementation of the XML DOM, as it is contained in msxml.dll, is extensive (and msxml3.dll is even more extensive). Since it encompasses many capabilities, it can become quite a chore to use. Also, since it is COM-based, it requires the C/C++ application developer to handle many of details normally associated with COM-based programming. This DLL simply begs for an MFC wrapper class to make it more manageable. I present here CXmlIF, which is my MFC wrapper class representation. CXmlIF is implemented in XmlIF.h (Listing 1) and XmlIF.cpp (Listing 2).

We start off with the #import msxml.dll directive in XmlIF.h. The #import directive is used to incorporate information from a type library, which is converted into C++ classes that describe COM interfaces to the components contained in the library. The compiler creates msxml.tlh when it handles this directive. A glance at this file illustrates how extensive msxml.dll is. We’ll use a subset of the interfaces from the type library header file for developing our wrapper class.

When CXmlIF object is constructed, CXmlIF::Initialize() is called. It is used to initialize the COM library through CoInitialize() and to create an instance of the DOM Document object, IXMLDOMDocument, which is returned through the interface pointer m_pDoc (using the COM method CoCreateInstance()). We use the flag, m_bInitialized, to determine if construction went OK and if the document object pointer is valid. The destructor calls UnInitialize() to clean up when the CXmlIF object is destroyed.

I load a document by calling CXmlIF::LoadDocument(const char* szURL). Since IXMLDOMDocument load() uses a variant type for the “file to load” argument, we have to perform a conversion on the original szURL argument. (Note: I decided not to use variant types for any of the arguments to the methods of this class, because most C/C++ developers are not accustomed to using this type, and it simply does not provide strong enough type checking when compiling. So, you’ll find that several of the CXmlIF class methods perform the conversions internally.) After converting the szURL argument to a variant, IXMLDOMDocument load() is called to load the document. We call CXmlIF::CheckLoad() to detect any parsing errors and then call CXmlIF::SetDocumentNode() to set the document node pointer, m_pDocNode. Internally, CXmlIF::SetDocumentNode() calls the document’s QueryInterface() to actually retrieve the document node pointer. If a parsing error is detected, then CXmlIF::LoadDocument() will fail and class member error variables will get set. The methods CXmlIF::GetErrReason(), CXmlIF::GetErrLine(), and CXmlIF::GetErrPos() can be used to determine what the problem was. The demo application illustrates the use of LoadDocument().

Saving a document is just as straightforward. Call CXmlIF::SaveDocument(const char* szURL). The character string to variant conversion is performed, and the content of the DOM object pointed to by m_pDoc is saved to the file specified.

As previously noted, the DOM represents a node hierarchy of the actual XML document. A document can contain a prolog, a body, and an epilog. The prolog usually contains a processing instruction node such as the following:

<?xml version="1.0" ?>

This node can be used to provide additional information to the processing application. A convenience method named CXmlIF::InsertProcessingInst() is provided with the CXmlIF class to handle the task of creating and appending this node to a document being created. Upon closer inspection of CXmlIF::InsertProcessingInst(), you’ll note that after creating the node with IXMLDOMDocument createProcessingInstruction(), we need to call IXMLDOMDocument appendChild(), because createProcessingInstruction() only creates the node. This is typically true of all the creation methods contained in the MSXML DLL.

The top node of the document is appropriately named the “document node.” The body of the XML document contains all the meat. There can only be one node at the top of the body. All other nodes must be descendents of this node. In fact, the node at the top of the body is a descendent of the document node. To add nodes to the document, we would start out by calling CXmlIF::CreateChild() using the document node as the first argument. For example, if we wanted to create a top-level element node named vehicle, we could use the following code:

CXmlIF      m_XmlIF;
m_XmlIF.SetDocumentNode();
MS_XML_NS::IXMLDOMNode* pDocNode
    = m_XmlIF.GetDocumentNode();
m_XmlIF.InsertProcessingInst();
MS_XML_NS::IXMLDOMNode* pNode = NULL;
if (pDocNode)
    pNode = m_XmlIF.CreateChild(
        pDocNode, _ TEXT("vehicle"));

We first instantiated the CXmlIF object, then set the document node pointer, and inserted the processing instruction node. If the document node pointer is valid, then we create the vehicle element. Internally, CXmlIF::CreateChild() uses IXMLDOMDocument::createNode() to actually create the new element. If successful, it then calls IXMLDOMDocument appendChild() to add the vehicle element node to the document. A pointer to the new element node is returned and assigned to pNode for later use.

After the above sequence of instructions, the document would look like the following:

<?xml version="1.0" ?>
<vehicle></vehicle>  or  <vehicle/>

It is a document with the processing instruction node in the prolog and a single vehicle element node in the body.

Now, let’s suppose that we wanted to add a type attribute with a value of "car" to the vehicle element. We would use CXxmlIF::CreateAttribute(). This method takes a pointer to the element node, an attribute node name, and an attribute value as arguments. Therefore, if the following code were executed after creating the vehicle element, as described above, then our type attribute node would be added to the element.

BSTR pBName = _TEXT("type");
BSTR pBValue = _TEXT("car");
MS_XML_NS::IXMLDOMAttribute* pAttr =
m_XmlIF.CreateAttribute(pNode, pBName, pBValue);
pAttr->Release();

Use a similar code sequence to add another attribute named year with a value of "1999" to the same vehicle element. Simply replace the pBName and pBValue pointers with the text strings _TEXT("year") and _TEXT("1999"). Now, our document looks like the following:

<?xml version="1.0" ?>
<vehicle type="car" year="1999"></vehicle>

or

<vehicle type="car" year="1999"/>

Note the line pAttr->Release() immediately following the call to CXmlIF::CreateAttribute(). I decided to require the calling application to release the COM resources allocated after the attribute node is created. This is because we cannot be sure what the calling application might want to do with the newly created attribute. If we call Release() within CXmlIF::CreateAttribute() and then needed to work with the newly created attribute, we would have to re-query the DOM to get a new pointer to the attribute node. In general, all the methods contained in CXmlIF that return a node pointer follow the format of requiring the calling application to call the COM Release() method directly when done with the node.

We can continue the process of adding nodes to the document by calling CreateChild() with the appropriate node pointer as the first argument. Recalling that we created the vehicle element node and returned a pointer to it through pNode, then the following code would create the child element node manufacturer.

MS_XML_NS::IXMLDOMNode* pChild =
m_XmlIF.CreateChild(pNode, _ TEXT("manufacturer"));

And we can set the text of the new manufacturer child element to "Ford" with CXmlIF::SetText() and the following code:

BSTR pBText = _TEXT("Ford");
m_XmlIF.SetText(pChild, pBText);

Similarly, we can add the model and price children to the vehicle element with the same code sequence, but different strings. The resulting document would look like this:

<?xml version="1.0" ?>
<vehicle type="car" year="1999">
    <manufacturer>Ford</manufacturer>
    <model>Mustang</model>
    <price/>
</vehicle>

That’s all there is to constructing a simple XML document using CXmlIF. Now, by calling CXmlIF::SaveDocument() with a filename argument, our example document will be saved to disk.

CXmlIF also includes methods for querying for elements and attributes of elements. We’ll use these methods in the next section when describing the demo application.

XML Demo

The demo application is a modified version of the XML-MFC Tree Control application from the MSDN website. This is a simple MFC C++ application that populates a Tree Control from a loaded XML document. The user can then navigate the XML document by expanding and collapsing nodes of the tree. Whereas my explanation in the prior section focused on building a simple XML document from scratch with CXmlIF, the demo application focuses on displaying an existing XML document from a file.

The original XML-MFC Tree Control application used raw MSXML DLL interface methods to access the DOM, and it was limited to displaying only elements and text in the tree control. I took this application and replaced the MSXML interfaces with comparable CXmlIF interfaces. I also added another class, CXmlIFCont. This class contains the instance of CXmlIF. It also cleans up the retrieval of string type information from the CXmlIF instance by using CString arguments in place of the BSTR arguments. Finally, I added the display of attributes and their values in the tree control so that the displayed tree represents a more complete picture of the XML document.

It’s simple to use the demo application. After you have invoked it, select the ‘Open...’ button in the top right corner of the dialog. This displays a standard Windows File Open dialog. Select one of the sample XML files (or any other XML file that you may already have). The application then uses CXmlIF::LoadDocument() to load the selected file to build an XML DOM object in memory. After loading the document, the demo application recursively parses all the nodes in the document and builds a tree representation of it, displaying all element nodes, element attributes, and element text. All element nodes are expandable and contractible by either clicking on the ‘+’ or ‘-’ next to it or by double clicking on the element itself. Attributes and text are displayed under their associated element. Each element, attribute, and text node is denoted by a 16x16 bitmap labeled ‘E’, ‘A’, or ‘T’.

Conclusion

This article only covers the basics and how to encapsulate some of the features of Microsoft’s XML DLL, specifically the DOM implementation, into an MFC class. I didn’t even come close to covering all the capabilities available in the DLL. One specification that I didn’t cover is the XSLT (Extensible Stylesheet Language Transforms). This allows you to transform documents into other documents. It is an extremely flexible tool. For example, it allows you to take an XML document that contains only data, apply a presentation transform (XSLT) to it, and output a viewable document that is dynamically built based on the content of the original document. What I’ve described only scratches the surface of what you can do with this DLL. Use the class that I have presented here as the basis for a complete MFC-based XML documentation tool. It greatly simplifies the handling of XML data when using Microsoft’s MSXML DLL.

Ben Campione is president of Systems Concepts, Inc., a Florida-based monitor and control automation software consulting firm. When not handling work-related tasks, Ben coaches and umpires youth baseball.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.