Source: dynamDOM.cpp

 

This application creates a simple, but complete, XML DOM object, with <root> as the document element. This element contains three child elements: <node1>, <node2>, and <node3>. The first child element contains character data. The second child element contains a CDATA section. The last child element contains three empty child elements: <subnode1>, <subnode2>,and <subnode3>.

Programmatically, the dynamDOM application performs the following steps:

  1. Creates an XML DOM instance (pXMLDom).

  2. Calls the CreateAndAddPINode method to create a processing instruction node targeted for XML 1.0, and add it to pXMLDom.

  3. Calls the CreateAndAddCommentNode method on the DOM object (pXMLDom) to create a comment node and then append it pXMLDom.

  4. Creates a <root> element (pRoot) as the document element, with an attribute whose name is "created" and value "using DOM". Adds this element (<root>) to the DOM object (pXMLDom).

  5. Creates a <node1> element with some character data as its content. Appends this element to the <root> element of the DOM object (pXMLDom).

  6. Creates a <node2> element that contains a CDATA section with markup text. Appends this element to the <root> element of the DOM object (pXMLDom).

  7. Creates a <node3> element that contains a DOM document fragment (pDF). This fragment contains three other empty child elements: <subNode1>, <subNode2>, and <subNode3>. The code then appends this element to the <root> element of the DOM object (pXMLDom).

  8. Saves this dynamically created DOM object to the project's main directory, and prints the XML data in the application console.

To add dynamDOM.cpp to the project

  1. Create a new C++ source file. For detailed instructions on how to do this, see Set Up My Visual C++ Project. Name the new file dynamDOM.cpp.

  2. Copy the C/C++ source code above and paste it into the source file you just created.

  3. Next, build and run the dynamDOM project. The result should be the output shown in the following topic.

C/C++ Source File (dynamDOM.cpp)

// DynamDOM.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <tchar.h>
#include <msxml6.h>

// Macro that calls a COM method returning HRESULT value.
#define CHK_HR(stmt)        do { hr=(stmt); if (FAILED(hr)) goto CleanUp; } while(0)

// Macro to verify memory allcation.
#define CHK_ALLOC(p)        do { if (!(p)) { hr = E_OUTOFMEMORY; goto CleanUp; } } while(0)

// Macro that releases a COM object if not NULL.
#define SAFE_RELEASE(p)     do { if ((p)) { (p)->Release(); (p) = NULL; } } while(0)

// Helper function to create a VT_BSTR variant from a null terminated string. 
HRESULT VariantFromString(PCWSTR wszValue, VARIANT &Variant)
{
    HRESULT hr = S_OK;
    BSTR bstr = SysAllocString(wszValue);
    CHK_ALLOC(bstr);
    
    V_VT(&Variant)   = VT_BSTR;
    V_BSTR(&Variant) = bstr;

CleanUp:
    return hr;
}

// Helper function to create a DOM instance. 
HRESULT CreateAndInitDOM(IXMLDOMDocument **ppDoc)
{
    HRESULT hr = CoCreateInstance(__uuidof(DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(ppDoc));
    if (SUCCEEDED(hr))
    {
        // these methods should not fail so don't inspect result
        (*ppDoc)->put_async(VARIANT_FALSE);  
        (*ppDoc)->put_validateOnParse(VARIANT_FALSE);
        (*ppDoc)->put_resolveExternals(VARIANT_FALSE);
        (*ppDoc)->put_preserveWhiteSpace(VARIANT_TRUE);
    }
    return hr;
}

// Helper that allocates the BSTR param for the caller.
HRESULT CreateElement(IXMLDOMDocument *pXMLDom, PCWSTR wszName, IXMLDOMElement **ppElement)
{
    HRESULT hr = S_OK;
    *ppElement = NULL;

    BSTR bstrName = SysAllocString(wszName);
    CHK_ALLOC(bstrName);
    CHK_HR(pXMLDom->createElement(bstrName, ppElement));

CleanUp:
    SysFreeString(bstrName);
    return hr;
}

// Helper function to append a child to a parent node.
HRESULT AppendChildToParent(IXMLDOMNode *pChild, IXMLDOMNode *pParent)
{
    HRESULT hr = S_OK;
    IXMLDOMNode *pChildOut = NULL;
    CHK_HR(pParent->appendChild(pChild, &pChildOut));

CleanUp:
    SAFE_RELEASE(pChildOut);
    return hr;
}

// Helper function to create and add a processing instruction to a document node.
HRESULT CreateAndAddPINode(IXMLDOMDocument *pDom, PCWSTR wszTarget, PCWSTR wszData)
{
    HRESULT hr = S_OK;
    IXMLDOMProcessingInstruction *pPI = NULL;

    BSTR bstrTarget = SysAllocString(wszTarget);
    BSTR bstrData = SysAllocString(wszData);
    CHK_ALLOC(bstrTarget && bstrData);
    
    CHK_HR(pDom->createProcessingInstruction(bstrTarget, bstrData, &pPI));
    CHK_HR(AppendChildToParent(pPI, pDom));

CleanUp:
    SAFE_RELEASE(pPI);
    SysFreeString(bstrTarget);
    SysFreeString(bstrData);
    return hr;
}

// Helper function to create and add a comment to a document node.
HRESULT CreateAndAddCommentNode(IXMLDOMDocument *pDom, PCWSTR wszComment)
{
    HRESULT hr = S_OK;
    IXMLDOMComment *pComment = NULL;

    BSTR bstrComment = SysAllocString(wszComment);
    CHK_ALLOC(bstrComment);
    
    CHK_HR(pDom->createComment(bstrComment, &pComment));
    CHK_HR(AppendChildToParent(pComment, pDom));

CleanUp:
    SAFE_RELEASE(pComment);
    SysFreeString(bstrComment);
    return hr;
}

// Helper function to create and add an attribute to a parent node.
HRESULT CreateAndAddAttributeNode(IXMLDOMDocument *pDom, PCWSTR wszName, PCWSTR wszValue, IXMLDOMElement *pParent)
{
    HRESULT hr = S_OK;
    IXMLDOMAttribute *pAttribute = NULL;
    IXMLDOMAttribute *pAttributeOut = NULL; // Out param that is not used

    BSTR bstrName = NULL;
    VARIANT varValue;
    VariantInit(&varValue);

    bstrName = SysAllocString(wszName);
    CHK_ALLOC(bstrName);
    CHK_HR(VariantFromString(wszValue, varValue));

    CHK_HR(pDom->createAttribute(bstrName, &pAttribute));
    CHK_HR(pAttribute->put_value(varValue));
    CHK_HR(pParent->setAttributeNode(pAttribute, &pAttributeOut));

CleanUp:
    SAFE_RELEASE(pAttribute);
    SAFE_RELEASE(pAttributeOut);
    SysFreeString(bstrName);
    VariantClear(&varValue);
    return hr;
}

// Helper function to create and append a text node to a parent node.
HRESULT CreateAndAddTextNode(IXMLDOMDocument *pDom, PCWSTR wszText, IXMLDOMNode *pParent)
{
    HRESULT hr = S_OK;    
    IXMLDOMText *pText = NULL;

    BSTR bstrText = SysAllocString(wszText);
    CHK_ALLOC(bstrText);

    CHK_HR(pDom->createTextNode(bstrText, &pText));
    CHK_HR(AppendChildToParent(pText, pParent));

CleanUp:
    SAFE_RELEASE(pText);
    SysFreeString(bstrText);
    return hr;
}

// Helper function to create and append a CDATA node to a parent node.
HRESULT CreateAndAddCDATANode(IXMLDOMDocument *pDom, PCWSTR wszCDATA, IXMLDOMNode *pParent)
{
    HRESULT hr = S_OK;
    IXMLDOMCDATASection *pCDATA = NULL;

    BSTR bstrCDATA = SysAllocString(wszCDATA);
    CHK_ALLOC(bstrCDATA);

    CHK_HR(pDom->createCDATASection(bstrCDATA, &pCDATA));
    CHK_HR(AppendChildToParent(pCDATA, pParent));

CleanUp:
    SAFE_RELEASE(pCDATA);
    SysFreeString(bstrCDATA);
    return hr;
}

// Helper function to create and append an element node to a parent node, and pass the newly created
// element node to caller if it wants.
HRESULT CreateAndAddElementNode(IXMLDOMDocument *pDom, PCWSTR wszName, PCWSTR wszNewline, IXMLDOMNode *pParent, IXMLDOMElement **ppElement = NULL)
{
    HRESULT hr = S_OK;
    IXMLDOMElement* pElement = NULL;

    CHK_HR(CreateElement(pDom, wszName, &pElement));
    // Add NEWLINE+TAB for identation before this element.
    CHK_HR(CreateAndAddTextNode(pDom, wszNewline, pParent));
    // Append this element to parent.
    CHK_HR(AppendChildToParent(pElement, pParent));

CleanUp:
    if (ppElement)
        *ppElement = pElement;  // Caller is repsonsible to release this element.
    else
        SAFE_RELEASE(pElement); // Caller is not interested on this element, so release it.

    return hr;
}

void dynamDOM()
{
    HRESULT hr = S_OK;
    IXMLDOMDocument *pXMLDom = NULL;
    IXMLDOMElement *pRoot = NULL;
    IXMLDOMElement *pNode = NULL;
    IXMLDOMElement *pSubNode = NULL;
    IXMLDOMDocumentFragment *pDF = NULL;

    BSTR bstrXML = NULL;
    VARIANT varFileName;
    VariantInit(&varFileName);

    CHK_HR(CreateAndInitDOM(&pXMLDom));

    // Create a processing instruction element.
    CHK_HR(CreateAndAddPINode(pXMLDom, L"xml", L"version='1.0'"));

    // Create a comment element.
    CHK_HR(CreateAndAddCommentNode(pXMLDom, L"sample xml file created using XML DOM object."));
    
    // Create the root element.
    CHK_HR(CreateElement(pXMLDom, L"root", &pRoot));

    // Create an attribute for the <root> element, with name "created" and value "using dom".
    CHK_HR(CreateAndAddAttributeNode(pXMLDom, L"created", L"using dom", pRoot));

    // Next, we will create and add three nodes to the <root> element.
    // Create a <node1> to hold text content.
    CHK_HR(CreateAndAddElementNode(pXMLDom, L"node1", L"\n\t", pRoot, &pNode));
    CHK_HR(CreateAndAddTextNode(pXMLDom, L"some character data", pNode));
    SAFE_RELEASE(pNode);

    // Create a <node2> to hold a CDATA section.
    CHK_HR(CreateAndAddElementNode(pXMLDom, L"node2", L"\n\t", pRoot, &pNode));
    CHK_HR(CreateAndAddCDATANode(pXMLDom, L"<some mark-up text>", pNode));
    SAFE_RELEASE(pNode);

    // Create <node3> to hold a doc fragment with three sub-elements.
    CHK_HR(CreateAndAddElementNode(pXMLDom, L"node3", L"\n\t", pRoot, &pNode));
    
    // Create a document fragment to hold three sub-elements.
    CHK_HR(pXMLDom->createDocumentFragment(&pDF));

    // Create 3 subnodes.
    CHK_HR(CreateAndAddElementNode(pXMLDom, L"subNode1", L"\n\t\t", pDF));
    CHK_HR(CreateAndAddElementNode(pXMLDom, L"subNode2", L"\n\t\t", pDF));
    CHK_HR(CreateAndAddElementNode(pXMLDom, L"subNode3", L"\n\t\t", pDF));
    CHK_HR(CreateAndAddTextNode(pXMLDom, L"\n\t", pDF));

    // Append pDF to <node3>.
    CHK_HR(AppendChildToParent(pDF, pNode));
    SAFE_RELEASE(pNode);

    // Add NEWLINE for identation before </root>.
    CHK_HR(CreateAndAddTextNode(pXMLDom, L"\n", pRoot));
    // add <root> to document
    CHK_HR(AppendChildToParent(pRoot, pXMLDom));

    CHK_HR(pXMLDom->get_xml(&bstrXML));
    printf("Dynamically created DOM:\n%S\n", bstrXML);

    CHK_HR(VariantFromString(L"dynamDOM.xml", varFileName));
    CHK_HR(pXMLDom->save(varFileName));
    printf("DOM saved to dynamDOM.xml\n");

CleanUp:
    SAFE_RELEASE(pXMLDom);
    SAFE_RELEASE(pRoot);
    SAFE_RELEASE(pNode);
    SAFE_RELEASE(pDF);
    SAFE_RELEASE(pSubNode);
    SysFreeString(bstrXML);
    VariantClear(&varFileName);
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = CoInitialize(NULL);
    if (SUCCEEDED(hr))
    {
        dynamDOM();
        CoUninitialize();
    }
    return 0;
}