MSXML Wrapper CMarkupMSXML

Since near the beginning of CMarkup, there has always been a companion MSXML wrapper version of CMarkup which provides a way to get started with MSXML in Visual Studio. This is great if you want to use MSXML but want to avoid the initial learning curve of programming to the COM interface, or if you are accustomed to CMarkup methods but want to take advantage of specific MSXML features.

Getting started with CMarkupMSXML

To get started, just add MarkupMSXML.cpp and MarkupMSXML.h to your Visual Studio project. All of the basic CMarkup methods are implemented, so for example you can load an XML file and get its root element tag name as follows:

#include "MarkupMSXML.h"
...
CMarkupMSXML xml;
if ( xml.Load("sample.xml") )
{
  xml.FindElem();
  CString csRootName = xml.GetTagName();
}

Versions of MSXML

CMarkupMSXML depends on the MSXML component installed on the machine. MSXML has been available on Windows machines since Internet Explorer 4.0 and Windows 95 OSR 2.5. It was not on Windows machines installed in the first year or two of Windows 95, but definitely available from 1998 forward. Through the years there have been numerous versions, some considered more reliable than others. The main ones are 1.0, 3.0, 4.0, 5.0, and 6.0.

Even though there are now several MSXML DLL versions around, there are only 2 primary versions of the XML interface. The 2 versions of the interface are provided in the MSXML and MSXML2 namespaces. The only one in MSXML 1.0 is MSXML, otherwise you should use MSXML2. The importance of these interface versions is that the one you compile with controls the functionality you have access to. When you compile with MSXML2, you can run with any of the MSXML DLLs that provide it.

Update December 17, 2008: With CMarkup release 10.1, at runtime CMarkupMSXML will dynamically load the MSXML component available on the machine, it will try MSXML 6.0, then 3.0, then 4.0 then 5.0. That is, unless you compile with MARKUP_MSXML1 defined, in which case it will compile for MSXML 1.0 and load MSXML 1.0 only.

Update September 10, 2009: With CMarkup release 11.2, during compile CMarkupMSXML imports the MSXML interface from msxml6.dll (since release 10.1 it defaulted to msxml3.dll, before that msxml.dll). By defining MARKUP_MSXML4 or MARKUP_MSXML3 or MARKUP_MSXML1, you can force it to import the corresponding DLL #import <msxml4.dll> or <msxml3.dll> or <msxml.dll>.

See also:

MSXML 3.0 in CMarkupMSXML
MSXML 4.0 in CMarkupMSXML

CMarkupMSXML has differences from CMarkup

MSXML is a validating parser, so when a document is loaded it is checked for all aspects of well-formedness including character encoding, and validated if there is a DTD. Also, if the parser returns an error, the document object is left empty (see When CMarkup Load Returns false), and parser error messages are generally quite different from those generated by CMarkup. Of course there is no support for HTML or ill-formed XML in CMarkupMSXML and whitespace is not preserved unless forced (see MSXML notes in Node Methods in CMarkup). CMarkupMSXML does not have any of the functions specific to the implementation of CMarkup such as offsets, index, file and encoding functions.

Other differences are kept to a minimum and many of the examples in the CMarkup documentation will work with CMarkupMSXML. The copy constructor works properly now in the release 8.0 developer version of CMarkupMSXML, but in previous releases and in the evaluation version making a copy of a CMarkupMSXML object creates references instead of copies which can lead to problems. Release 8.0 also brought absolute path behavior in line with CMarkup (see Paths In CMarkup).

Using MSXML-specific features

CMarkupMSXML has a public member m_pDOMDoc to provide access to the document for utilizing additional features of MSXML beyond the encapsulated methods.

Update August 17, 2005: CMarkupMSXML release 8.1 has the SetMainPosPtr method for setting the position back into the CMarkupMSXML object after external queries like the following two examples. The first example finds a single element based on the MSXML selectSingleNode function.

CMarkupMSXML xml;
xml.SetDoc( _T("<TOP><MID><LEAF1/></MID><MID/><MID><LEAF2/></MID></TOP>") );
_bstr_t bstrQuery( _T("/TOP/MID/LEAF2") );
xml.SetMainPosPtr( xml.m_pDOMDoc->selectSingleNode(bstrQuery) );
// xml.GetTagName() == _T("LEAF2")

Also with MSXML 3.0 forward, you can use XPath to query the document for a collection of nodes. First you need to set the SelectionLanguage.

CMarkupMSXML xml;
xml.SetDoc( _T("<Admin><Area AreaName=\"a\"/></Admin>") );
xml.m_pDOMDoc->setProperty( _T("SelectionLanguage"), _T("XPath") );
CString csQuery = _T("/Admin/Area[string-length(@AreaName) = 1]");
MSXMLNS::IXMLDOMNodeListPtr pNodes;
pNodes = xml.m_pDOMDoc->selectNodes( csQuery );
int nSelectCount = pNodes->Getlength();
xml.SetMainPosPtr( pNodes->Getitem(0) );
// xml.GetTagName() == _T("Area")

Update September 10, 2009: With CMarkup release 11.2 using MSXML 3.0 forward, you can use the Transform method to perform a transformation with MSXML's XSLT.

CMarkupMSXML xml(
  MCD_T("<list><i><n>sit</n></i><i><n>stand</n></i></list>") );
CMarkupMSXML xsl(
  MCD_T("<xsl:stylesheet version=\"1.0\" ")
  MCD_T("xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\r\n")
  MCD_T("<xsl:template match=\"/\">\r\n")
  MCD_T("<html><body><table>\r\n")
  MCD_T("  <xsl:for-each select=\"/*/i\">\r\n")
  MCD_T("  <tr><td><xsl:value-of select=\"n\"/></td></tr>\r\n")
  MCD_T("  </xsl:for-each>\r\n")
  MCD_T("</table></body></html>\r\n")
  MCD_T("</xsl:template>\r\n")
  MCD_T("</xsl:stylesheet>\r\n")
  );

CString strResult = xml.Transform( xsl );

The result is the following:

<html><body><table>
<tr><td>sit</td></tr>
<tr><td>stand</td></tr>
</table></body></html>

 

comment posted MSXML Processing Instruction

Tien 16-Jul-2008

I use CMarkupMSXML with msxml4.dll and Visual C++. I got an error when trying to create processing indtruction <?xml version="1.0"?> and don't know how to resolve it. Here is what happens:

CMarkupMSXML Xmldoc;
Xmldoc.ResetPos();
Xmldoc.InsertNode(CMarkupMSXML::MNT_PROCESSING_INSTRUCTION,
    "xml" ); ///problem here
Xmldoc.SetAttrib( "version", "1.0" );

Traced through MarkupMSXML.cpp

pPI = m_pDOMDoc->createProcessingInstruction(
    ToBSTR(strTarget), ToBSTR(strData) );

Then failed in msxml.tli at:

inline IXMLDOMProcessingInstructionPtr
    IXMLDOMDocument::createProcessingInstruction (
    _bstr_t target, _bstr_t data )

With error message because the HRESULT was not S_OK:

Unhandled exception at 0x7c812a7b in TestMarkup.exe: Microsoft C++
exception: _com_error @ 0x0012ee04.

MSXML does not allow the XML declaration to be created without the version in it from the beginning. So:

Xmldoc.InsertNode( CMarkupMSXML::MNT_PROCESSING_INSTRUCTION,
    "xml version = \"1.0\"" );

CMarkupMSXML puts the version into the data argument of the call to createProcessingInstruction.