Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Xerces for C++ Using Visual C++, Part 2

4.00/5 (9 votes)
22 Jan 2009CPOL1 min read 98.9K  
Create, save, and display DOM documents using C++.

Introduction

This tutorial shows you how to create an XML DOM document using Xerces for C++ using Visual Studio 9, and also how to output the results to a console, or save to a file. This tutorial produces the following output:

XML
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Hello_World>
    <!-- Dates are formatted mm/dd/yy. Don't forget XML is case-sensitive. -->
    <data date="01/22/09" float="3.14159265" integer="65536">
        <row index="1">Comments and data can also go in text nodes.</row>
        <row description="The value of PI">3.1415926535897932384626433832795</row>
        <row website="http://www.ted.com.au/">TED - ideas worth sharing.</row>
    </data>
</Hello_World>

This tutorial assumes you have already downloaded the Xerces binaries, and have correctly configured your Visual Studio environment. If you need help with this, read my previous tutorial on the subject.

Include Files

For this tutorial, you will need the following includes at a minimum:

C++
// Mandatory for using any feature of Xerces.
#include <xercesc/util/PlatformUtils.hpp>

// Use the Document Object Model (DOM) API
#include <xercesc/dom/DOM.hpp>

// Required for outputing a Xerces DOMDocument
// to a standard output stream (Also see: XMLFormatTarget)
#include <xercesc/framework/StdOutFormatTarget.hpp>

// Required for outputing a Xerces DOMDocument
// to the file system (Also see: XMLFormatTarget)
#include <xercesc/framework/LocalFileFormatTarget.hpp>

This tutorial also uses the following includes for creating sample data:

C++
// (optional) The following includes
// are only used to create sample data in this tutorial.
#include <time.h>
#include <string>
#include <sstream>

Namespaces

The following line (without semi-colon) will expand to use the correct Xerces namespace. The alternative is to prefix all Xerces code and functions with the XERCES_CPP_NAMESPACE:: namespace.

C++
// Define namespace symbols
XERCES_CPP_NAMESPACE_USE

Initializing Xerces and the DOM

Initialize Xerces, and get the DOM implementation used for creating DOM documents:

C++
// Initilize Xerces.
XMLPlatformUtils::Initialize();

// Pointer to our DOMImplementation.
DOMImplementation * pDOMImplementation = NULL;

// Get the DOM Implementation (used for creating DOMDocuments).
// Also see: http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html
pDOMImplementation = 
  DOMImplementationRegistry::getDOMImplementation(XMLString::transcode("core"));

Note: Both Xerces and Xalan follow the official specifications for the XML and XSLT languages. These can be found at the W3C.

Creating the DOM Document Using Xerces for C++

The following code block creates a DOM document and fills in some sample data. The sample data starts life as integers, floats, dates, or strings, but ultimately, everything must be converted to a wide-character string as required by Xerces.

C++
// Pointer to a DOMDocument.
DOMDocument * pDOMDocument = NULL;

/*
Create an empty DOMDocument. When an empty document 
is created specify the namespace of the document (optional)
and also the root node. Both of these values can be any 
valid text (ie. no spaces or weird characters).
The "root node" in this example is "Hello_World". 
(XML is case-sensitive) This root node 
*owns everything* beneath it, just as if it were a separate object... 
in fact, it is. It's defined by the 'DOMNode' class.

NOTE: The namespace in this example happens to be a website
      within the 'example.com' domain [RFC2606]
      However, it does NOT have to be a domain name. 
      It could be your company name, or the name of your dog.
      It's just that domain names tend to be unique. 
      Since any name will do, there does not even have to 
      be a website at the address specified.
*/
pDOMDocument = pDOMImplementation->createDocument(L"schemas.example.com/2008/", 
                                                  L"ex:Hello_World", 0);

// If you're not interested in namespaces (and most of the time I'm not),
// just use the following line instead of the one above...
pDOMDocument = pDOMImplementation->createDocument(0, L"Hello_World", 0);


/*
=============================================================
Anything below this point is optional, 
although it would be a pretty boring and empty document!.
=============================================================

Since we are going to add nodes to the document's root, 
we need a pointer to this root node/element (which was
previously created when the document was created).

    Note: Due to their close relationship, 
    the words "node" and "element" are often used
    interchangably. Nodes are the base class, 
    and Elements are the specilizations.
*/
DOMElement * pRootElement = NULL;
pRootElement = pDOMDocument->getDocumentElement();


// Create a Comment node, and then append this to the root element.
DOMComment * pDOMComment = NULL;
pDOMComment = pDOMDocument->createComment(L"Dates are formatted mm/dd/yy." 
              L" Don't forget XML is case-sensitive.");
pRootElement->appendChild(pDOMComment);


// Create an Element node, then fill in some attributes,
// and then append this to the root element.
DOMElement * pDataElement = NULL;
pDataElement = pDOMDocument->createElement(L"data");

    // Copy the current system date to a buffer, then set/create the attribute.
    wchar_t wcharBuffer[128];
    _wstrdate_s(wcharBuffer, 9);
    pDataElement->setAttribute(L"date", wcharBuffer);

    // Convert an integer to a string, then set/create the attribute.
    _itow_s(65536, wcharBuffer, 128, 10);
    pDataElement->setAttribute(L"integer", wcharBuffer);

    // Convert a floating-point number to a wstring,
    // then set/create the attribute.
    std::wstringstream    myStream;
        myStream.precision(8);
        myStream.setf(std::ios_base::fixed, std::ios_base::floatfield);
        myStream << 3.1415926535897932384626433832795;
    const std::wstring ws(myStream.str());
    pDataElement->setAttribute(L"float", ws.c_str());
    
    // Append 'pDataElement' to 'pRootElement'.
    pRootElement->appendChild(pDataElement);


// Create an Element node, then fill in some attributes, add some text,
// then append this to the 'pDataElement' element.
DOMElement * pRow = NULL;
pRow = pDOMDocument->createElement(L"row");

    // Create some sample data.
    _itow_s(1, wcharBuffer, 128, 10);
    pRow->setAttribute(L"index", wcharBuffer);

    /*
    Create a text node and append this as well. Some people 
    prefer to place their data in the text node
    which is perfectly valid, others prefer to use 
    the attributes. A combination of the two is quite common.
    */
    DOMText* pTextNode = NULL;
    pTextNode = pDOMDocument->createTextNode(L"Comments and" 
                L" data can also go in text nodes.");
    pRow->appendChild(pTextNode);

    pDataElement->appendChild(pRow);


// Create another row (this time putting data and descriptions into different places).
pRow = pDOMDocument->createElement(L"row");
    pRow->setAttribute(L"description", L"The value of PI");
    pTextNode = pDOMDocument->createTextNode(L"3.1415926535897932384626433832795");
    pRow->appendChild(pTextNode);
    pDataElement->appendChild(pRow);


// Create another row.
pRow = pDOMDocument->createElement(L"row");
    pRow->setAttribute(L"website", L"http://www.ted.com.au/");
    pTextNode = pDOMDocument->createTextNode(L"TED - ideas worth sharing.");
    pRow->appendChild(pTextNode);
    pDataElement->appendChild(pRow);

Output the DOM Document to the Standard Output Stream

C++
void DoOutput2Stream(DOMDocument* pmyDOMDocument)
{
    DOMImplementation    *pImplement    = NULL;
    DOMWriter        *pSerializer    = NULL;
    XMLFormatTarget        *pTarget    = NULL;

    /*
    Return the first registered implementation that has
    the desired features. In this case, we are after
    a DOM implementation that has the LS feature... or Load/Save.
    */
    pImplement = DOMImplementationRegistry::getDOMImplementation(L"LS");

    /*
    From the DOMImplementation, create a DOMWriter.
    DOMWriters are used to serialize a DOM tree [back] into an XML document.
    */
    pSerializer = ((DOMImplementationLS*)pImplement)->createDOMWriter();

    /*
    This line is optional. It just sets a feature
    of the Serializer to make the output
    more human-readable by inserting line-feeds and tabs, 
    without actually inserting any new nodes
    into the DOM tree. (There are many different features to set.) 
    Comment it out and see the difference.
    */
    pSerializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);

    /*
    Choose a location for the serialized output. The 3 options are:
        1) StdOutFormatTarget     (std output stream -  good for debugging)
        2) MemBufFormatTarget     (to Memory)
        3) LocalFileFormatTarget  (save to file)
        (Note: You'll need a different header file for each one)
    */
    pTarget = new StdOutFormatTarget();
    
    // Write the serialized output to the target.
    pSerializer->writeNode(pTarget, *pmyDOMDocument);
}

Save the DOM Document to the File System

Similar to the above section, use the following code to save a DOM document to the file system:

C++
void DoOutput2File(DOMDocument* pmyDOMDocument, const wchar_t * FullFilePath )
{

    DOMImplementation    *pImplement        = NULL;
    DOMWriter            *pSerializer    = NULL;
    XMLFormatTarget        *pTarget        = NULL;

    /*
    Return the first registered implementation that 
    has the desired features. In this case, we are after
    a DOM implementation that has the LS feature... or Load/Save.
    */
    pImplement = DOMImplementationRegistry::getDOMImplementation(L"LS");

    /*
    From the DOMImplementation, create a DOMWriter.
    DOMWriters are used to serialize a DOM tree [back] into an XML document.
    */
    pSerializer = ((DOMImplementationLS*)pImplement)->createDOMWriter();


    /*
    This line is optional. It just sets a feature 
    of the Serializer to make the output
    more human-readable by inserting line-feeds, 
    without actually inserting any new elements/nodes
    into the DOM tree. (There are many different features to set.) 
    Comment it out and see the difference.
    */
    pSerializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);


    /*
    Choose a location for the serialized output. The 3 options are:
        1) StdOutFormatTarget     (std output stream -  good for debugging)
        2) MemBufFormatTarget     (to Memory)
        3) LocalFileFormatTarget  (save to file)
        (Note: You'll need a different header file for each one)
        Don't forget to escape file-paths with a backslash character, or
        just specify a file to be saved in the exe directory.
        
          eg. c:\\example\\subfolder\\pfile.xml

    */
    pTarget = new LocalFileFormatTarget(FullFilePath);


    // Write the serialized output to the target.
    pSerializer->writeNode(pTarget, *pmyDOMDocument);
}

Clean Up

Clean up as usual.

C++
// Cleanup.
p_DOMDocument->release();
XMLPlatformUtils::Terminate();

Known Problems with Visual Studio 2008

I've encountered the following problem compiling and linking with Xerces using Visual Studio v9 (2008). To resolve the following error...

error LNK2001: unresolved external symbol
"__declspec(dllimport) public: static wchar_t *
__cdecl xercesc_2_7::XMLString::transcode(char const * const)" (
__imp_?transcode@XMLString@xercesc_2_7@@SAPA_WQBD@Z)

...modify the following C++ language compiler option:

C++
/Zc:wchar_t (wchar_t Is Native Type)

History

  • Initial release: 2009-01-22.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)