- Introduction
- C++ with MSXML
- Java with JAXP and Xalan
- Conclusion
- For More Information
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 8299.
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".