Producing XML
After the initial housekeeping setup, XML can be produced using the repeated invocation of only a few methods. The basic approach involves calling the DOM Document interface's create method for whatever flavor of DOM Node we're creating, and then attaching the newly created node to its parent node. The sample program ProduceXML.cpp follows this approach, with only a few deviations when it produces attributes. (Click here for the source file.)
1 /* ProduceXML.cpp 2 3 This program illustrates basic DOM operations involved 4 with creating an XML document using MSXML's DOM 5 implementation. 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 << "Sample ProduceXML Program" << 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 1; 30 } 31 32 // Main try block for MSXML DOM operations 33 try 34 { 35 // MSXML COM smart pointers 36 // Use the Dccument2 class to enable schema validation 37 IXMLDOMDocument2Ptr spDocOutput; 38 IXMLDOMElementPtr spElemRoot; 39 IXMLDOMElementPtr spElemL1; 40 IXMLDOMElementPtr spElemL2; 41 IXMLDOMProcessingInstructionPtr spXMLDecl; 42 IXMLDOMAttributePtr spSchemaLocationAttribute; 43 IXMLDOMTextPtr spText; 44 45 // Create the COM DOM Document object 46 hResult = spDocOutput.CreateInstance(__uuidof(DOMDocument40)); 47 48 if FAILED(hResult) 49 { 50 cerr << "Failed to create Document instance" << endl; 51 return 1; 52 } 53 54 // Create the XML Declaration as a Processing Instruction 55 // and append it to the document node 56 spXMLDecl = spDocOutput->createProcessingInstruction("xml", 57 "version=\"1.0\" encoding=\"UTF-8\""); 58 spDocOutput->appendChild(spXMLDecl); 59 60 // Create the root element and append it to the Document 61 spElemRoot = spDocOutput->createElement("SampleDocumentElement"); 62 spDocOutput->appendChild(spElemRoot); 63 64 // Add the schema attributes to the Root Element 65 // First declare the xsi namespace 66 spElemRoot->setAttribute("xmlns:xsi", 67 "http://www.w3.org/2001/XMLSchema-instance"); 68 69 // Next set the schema location 70 // MSXML requires that namespace qualified Attributes 71 // be created as Nodes, then set 72 _variant_t varType((short)NODE_ATTRIBUTE); 73 spSchemaLocationAttribute = spDocOutput->createNode( 74 varType,"xsi:noNamespaceSchemaLocation", 75 "http://www.w3.org/2001/XMLSchema-instance"); 76 spElemRoot->setAttributeNode(spSchemaLocationAttribute); 77 78 // We can finally set it now 79 spElemRoot->setAttribute("xsi:noNamespaceSchemaLocation", 80 "SampleSchema.xsd"); 81 82 // Create the first FirstLevelChild element and append 83 // to the document element 84 spElemL1 = spDocOutput->createElement("FirstLevelChild"); 85 spElemRoot->appendChild(spElemL1); 86 87 // Add the Attribute1 attribute 88 spElemL1->setAttribute("Attribute1","Attribute Value"); 89 90 // Create the SecondLevelChild element and append 91 // to the FirstLevelChild 92 spElemL2 = spDocOutput->createElement("SecondLevelChild"); 93 spElemL1->appendChild(spElemL2); 94 95 // Add text for the SecondLevelChild by creating a Text 96 // node and appending it 97 spText = spDocOutput->createTextNode("Element Text"); 98 spElemL2->appendChild(spText); 99 100 // Create the empty second FirstLevelChild element and 101 // append to the document element. Note that we can reuse 102 // this pointer. 103 spElemL1 = spDocOutput->createElement("FirstLevelChild"); 104 spElemRoot->appendChild(spElemL1); 105 106 // Save the output XML Document 107 hResult = spDocOutput->save("SampleDccument.xml"); 108 109 if FAILED(hResult) 110 { 111 cerr << "Failed to save document" << endl; 112 return 1; 113 } 114 115 } // End of try block 116 117 // Catch COM exceptions 118 catch (_com_error &e) 119 { 120 cerr << "COM Error" << endl; 121 cerr << "Message = " << e.ErrorMessage() << endl; 122 return 1; 123 } 124 125 // Release COM resources 126 CoUninitialize(); 127 128 cout << endl << endl << "Successful Completion" << endl; 129 130 return 0; 131 }
The initial program logic up to line 54 is nearly identical to the beginning of the ConsumeXML program. The COM library is readied and the DOM Document is created. The first DOM Node that must be created is the XML declaration in the first line of the sample document. Even though it isn't technically a processing instruction according to the XML Recommendation, we create in the DOM a ProcessingInstruction node and append it to the Document element as its first child. The DOM allows programmers to insert nodes as well as append them, but for simplicity and consistency I always append them when building a document tree from scratch.
After creating the XML declaration, the program then creates the Document element SampleDocumentElement and appends it to the document at lines 6062. After that, we see the two basic ways to create attributes using the MSXML DOM implementation. We must create two attributes. The first is the xmlns namespace attribute in line 3 of the sample document. This attribute is easily created by a single call to the document element's setAttribute method at lines 6467. The second attribute in line 4 of the sample document specifies the location of the schema. This attribute is a bit more difficult to deal with in MSXML because, being associated with the XMLSchema-instance namespace, it's namespace qualified. The DOM specifies a setAttributeNS method that's implemented by several other DOM APIs, but not by MSXML. In MSXML, a namespace-qualified attribute must first be created using the Document interface's createNode method, specifying a node type of Attribute. Then, once the attribute node is created as a namespace-qualified node, it can be assigned to the root element using the setAttributeNode method. This approach is demonstrated in lines 6980. Notice that at line 72 the node type is created as a VARIANT, and passed in the createNode invocation of line 73.
Lines 90 through 104 create the remaining elements and attributes using the same basic methods. The main thing to keep track of here is being sure that newly created nodes are attached to the proper parent. At line 107, the DOM document is saved to disk using the Document interface's save method.