Home > Articles

  • Print
  • + Share This
From the author of

C++ with MSXML

Listing 4 (DOMAndXSLT.CPP) presents the basic operations for performing an XSLT transformation with DOM documents as source and result using C++ with MSXML. If you read my previous articles in this series or are otherwise familiar with using MSXML's DOM from C++, you'll find little that's new in the code. In fact, most of the code that deals with the transformation is isolated in lines 82–99.

Listing 4 DOMAndXSLT.CPP





 1 /* DOMAndXSLT.cpp
 2
 3 This program illustrates basic techniques for transforming
 4 a DOM source document into a DOM result document using
 5 the MSXML transformNodeToObject method and XSLT.
 6
 7 Michael C. Rawlins, 2003, for InformIT
 8
 9 */
 10
 11 #include <iostream.h>
 12
 13 #import <msxml4.dll>
 14 using namespace MSXML2;
 15
 16 int main(int argc, char* argv[])
 17 {
 18
 19 // Local variables and initializations
 20 HRESULT hResult = S_OK;
 21
 22 cout << endl << "DOM and XSLT Demonstration" << endl << endl;
 23 
 24 // Initialize the COM library
 25 hResult = CoInitialize(NULL);
 26 if (FAILED(hResult))
 27 {
 28  cerr << "Failed to initialize COM environment" << endl;
 29  return 0;
 30 }
 31
 32 // Main try block for MSXML DOM operations
 33 try
 34 {
 35  // MSXML COM smart pointers
 36  // Use the Document2 class to enable schema validation
 37  IXMLDOMDocument2Ptr spDocSource;
 38  IXMLDOMDocument2Ptr spDocResult;
 39  IXMLDOMDocument2Ptr spDocStylesheet;
 40  struct IDispatch * pDispatch;
 41
 42  // Create the COM DOM Document objects
 43  hResult = spDocSource.CreateInstance(__uuidof(DOMDocument40));
 44  if FAILED(hResult)
 45  {
 46   cerr << "Failed to create Source Document instance" << endl;
 47   return 1;
 48  }
 49
 50  hResult = spDocResult.CreateInstance(__uuidof(DOMDocument40));
 51  if FAILED(hResult)
 52  {
 53   cerr << "Failed to create Result Document instance" << endl;
 54   return 1;
 55  }
 56
 57  hResult = spDocStylesheet.CreateInstance(__uuidof(DOMDocument40));
 58  if FAILED(hResult)
 59  {
 60   cerr << "Failed to create Stylesheet Document instance" << endl;
 61   return 1;
 62  }
 63
 64  // Load the source document
 65  spDocSource->async = VARIANT_FALSE; 
 66  hResult = spDocSource->load("HelloWorld.xml");
 67  if( hResult != VARIANT_TRUE)
 68  {
 69   cout << "Error parsing HelloWorld.xml" << endl;
 70   return 1;
 71  }
 72
 73  // Load the stylesheet document
 74  spDocStylesheet->async = VARIANT_FALSE; 
 75  hResult = spDocStylesheet->load("HelloWorld.xsl");
 76  if( hResult != VARIANT_TRUE)
 77  {
 78   cout << "Error parsing HelloWorld.xsl" << endl;
 79   return 1;
 80  }
 81
 82  // The transformNodeToObject method requires a COM VARIANT
 83  // as the second argument. We query the result document
 84  // to get a pointer to its implementation of the
 85  // IDispatch interface.
 86  spDocResult->QueryInterface(IID_IDispatch, (void **) &pDispatch);
 87  VARIANT vResultDoc;
 88  vResultDoc.vt = VT_DISPATCH;
 89  vResultDoc.pdispVal = pDispatch;
 90
 91  // Perform the transformation using the source document's
 92  // transformNodeToObject method
 93  hResult =
 94    spDocSource->transformNodeToObject(spDocStylesheet, vResultDoc);
 95  if FAILED(hResult)
 96  {
 97   cout << "Error in performing transformation" << endl;
 98   return 1;
 99  }
100
101  // Save the result document
102  hResult = spDocResult->save("ResultDocument.xml");
103  if FAILED(hResult)
104  {
105   cerr << "Failed to save result document" << endl;
106   return 1;
107  }
108
109 } // End of try block
110 
111 // Catch COM exceptions
112 catch (_com_error &e)
113 {
114  cerr << "COM Error" << endl;
115  cerr << "Message = " << e.ErrorMessage() << endl;
116  return 1;
117 }
118
119 // Release COM resources
120 CoUninitialize();
121
122 cout << endl << endl << "Successful Completion" << endl;
123
124 return 0;
125 }

The transformation is performed by a call to a single method of the source document object, transformNodeToObject. This method is an extension to the standard DOM Document interface. It takes two arguments:

  • The stylesheet, passed as a DOM Document

  • The result document

The only slightly tricky part of this method invocation is that the result document argument is passed as a COM VARIANT. Only two fields, vt for the variant type and pdispVal as an address, are required in the VARIANT structure. The method I show here for setting up the VARIANT will probably be second nature to seasoned COM programmers. For those not familiar with Microsoft's Component Object Model, it isn't feasible within the scope of this article to fully explain what's going on. Accept that this is just one of those odd incantations you must perform when pursuing the black art of COM programming, and cut, paste, and edit the four lines starting with the QueryInterface call.

NOTE

transformNodeToObject works just fine if the VARIANT is assigned a type of VT_UNKNOWN and pdispVal is set to spDocResult. However, the MSXML SDK sample programs use the QueryInterface method to get the address of the result document's IDispatch interface, and it seems like better COM programming practice to do it that way. Who am I to quibble with the experts?

The only other thing to note about this MSXML example is that this approach, as coded above, produces a result document with double-byte UTF-16 character encoding, the MSXML default. There are various ways around this issue if you want to produce the more frequently used single-byte UTF-8 encoding. The easiest is probably to use the xsl:output element in the stylesheet, setting the encoding attribute with a value of "UTF-8".

  • + Share This
  • 🔖 Save To Your Account