Channels ▼
RSS

JVM Languages

C++, Java, & XML Processing


XML Creation in C++

The C++ DOM API provides an implementation-independent starting point via the DOMImplementationRegistry static class. A call to the getDOMImplementation method on this class returns a DOMImplementation object. Calling createDocument on this object creates and returns a DOMDocument object, which is subsequently used to build Node objects for your DOM hierarchy. The XML namespace, DTD, and root node name are passed as parameters to this method.

Example 2 illustrates the method calls just described and begins the creation of a document representing the XML in Example 1. The result is a mostly empty DOM document; all that exists is a root node named Customer, identified as the object rootElem.

Example 2: Creating an XML DOM in C++.

// Create a new XML DOM Document
DOMImplementation* dom 
    = DOMImplementationRegistry::getDOMImplementation("Core");
DOMDocument doc = dom->createDocument(
    NULL,       // namespace URI.
    "Customer", // root element name
    NULL );     // DTD
// Get the root element Node object
DOMElement* rootElem = doc->getDocumentElement();

Adding a node to the document requires a series of method calls; see Example 3. First, a DOMElement object is created via the method DOMDocument::createElement, with the node name LastName. Next, a DOMText node is created by calling DOMDocument::createTextNode with the text "Bruno". The text node is made a child of the new element object, and the element object is made a child of the document's root element.

Example 3: Adding elements and serializing the DOM in C++.

// Create the LastName element node
DOMElement* elem = doc->createElement( "LastName" );
// Create the textual content for the element
DOMText* text = doc->createTextNode( "Bruno" );
// Make the text node a child of the element node
elem->appendChild( text );
// Make the new element a child of the root node "Customer"
rootElem->appendChild( elem );
// Get a writer object to serialize to disk
DOMWriter* serializer = dom->createDOMWriter();
// Set the file name
XMLFormatTarget* target 
    =  new LocalFileFormatTarget( "c:\\mydocument.xml" );
// Write the XML to the file
serializer->writeNode(target, *doc);

Serialization of the XML is started by obtaining a DOMWriter object by calling the DOMImplemention::createDOMWriter method. Finally, the XML is written to a file by calling DOMWrite::writeNode, passing the filename (as an XMLFileFormat object) and the DOMDocument object as parameters.

For the C++ client application, all of the C++ code for creating and serializing the requested XML is contained within the CClientXMLGen class; see Listing One. The customer data is contained within an object of class Client, but the code can easily be modified to use a class containing your own application data.

Listing One

CClientXMLGen::CClientXMLGen() 
{
  try {
    XMLPlatformUtils::Initialize();
    m_doc = NULL;
  }
  catch(const XMLException& e) {
    char *pMsg = XMLString::transcode(e.getMessage());
    cerr << "Error during Xerces-c Initialization.\n"
         << "  Exception message:"
         << pMsg;
    XMLString::release(&pMsg);
  }
}
CClientXMLGen::~CClientXMLGen() 
{
  delete m_doc;
  XMLPlatformUtils::Terminate();
}
bool CClientXMLGen::CreateXMLDocument(Client* pClient)
{
  try {
    // Create a new XML DOM Document
    DOMImplementation* dom 
      = DOMImplementationRegistry
        ::getDOMImplementation(X("Core"));
    m_doc = dom->createDocument(
        NULL,        // namespace
        X("Client"), // root element name
        NULL);       // DTD.
    // Create root element and add the client 
    // details as child nodes with text nodes
    DOMElement* rootElem = m_doc->getDocumentElement();
    AddElem( rootElem, 
        X("LastName"), X(pClient->GetLastName()) );
    AddElem( rootElem, 
        X("FirstName"), X(pClient->GetFirstName()) );
    AddElem( rootElem, 
        X("Street"), X(pClient->GetStreet()) );
    AddElem( rootElem, 
        X("City"), X(pClient->GetCity()) );
    AddElem( rootElem, 
        X("Zip"), X(pClient->GetZip()) );
    AddElem( rootElem, 
        X("State"), X(pClient->GetState()) );
    // Create another element for the credit card information
    DOMElement* CreditCardElem 
        = m_doc->createElement( X("CreditCard") );
    rootElem->appendChild( CreditCardElem );
    AddElem( CreditCardElem, 
        X("Number"), 
        X(pClient->GetCreditCard()->GetCardNumber()) );
    AddElem( CreditCardElem, 
        X("Type"), 
        X(pClient->GetCreditCard()->GetCardType()) );
    AddElem( CreditCardElem, 
        X("Holder"), 
        X(pClient->GetCreditCard()->GetCardHolder()) );
    AddElem( CreditCardElem, 
        X("Expiration"), 
        X(pClient->GetCreditCard()->GetExpDate()) );
    return true;
  }
  catch (XMLException& e) {
    char *pMsg = XMLString::transcode(e.getMessage());
    cerr << "Exception when creating the Client XML:\n"
         << "  Exception message:"
         << pMsg;
    XMLString::release(&pMsg);
  }
    return false;
}
bool CClientXMLGen::SerializeXMLDocument( char* filename)
{
  try {
    if ( m_doc != NULL )
    {
      // Write the XML out as a file
      DOMImplementation* impl 
        = DOMImplementationRegistry
          ::getDOMImplementation(X("LS"));
      DOMWriter* serializer 
          = ((DOMImplementationLS*)impl)->createDOMWriter();
      serializer->setEncoding(NULL);
      XMLFormatTarget* target
          = new LocalFileFormatTarget( filename );
      // get the DOM representation
      serializer->writeNode(target, *m_doc);
      delete serializer;
      delete target;
      return true;
    }
  }
  catch (XMLException& e) {
        char *pMsg = XMLString::transcode(e.getMessage());
        cerr << "Exception when serializing the XML:\n"
             << "  Exception message:"
             << pMsg;
        XMLString::release(&pMsg);
    }
    return false;
}
void CClientXMLGen::AddElem(DOMElement* pRootElem, 
  const XMLCh * elemName, const XMLCh * elemVal)
{
    DOMElement* elem = m_doc->createElement( elemName );
    DOMText* text = m_doc->createTextNode( elemVal );
    elem->appendChild( text );
    pRootElem->appendChild( elem );
}

XML Creation in Java

Creating a DOM in Java starts with the DocumentBuilder class. First, obtain a reference to this class by calling DOMBuilderFactory.newDocumentBuilder. Next, create a Document object by calling DocumentBuilder.newDocument. The process of creating child nodes and assembling the object hierarchy in Java is similar to the process followed in the C++ code.

Serialization of the XML begins with a java.io.FileOutputStream object, which specifies the filename. This object is passed into the constructor of a new org.apache.xml.serialize.XMLSerializer object. A call to XMLSerializer.serialize is all it takes to write the XML DOM to a file on disk. Listing Two shows the complete process of creating a DOM in Java.

Listing Two

DWORD WINAPI ResponseThreadProc(LPVOID lpParam) {
  HANDLE hTermEvt = (HANDLE)lpParam;
  WIN32_FIND_DATA findFileData;
  HANDLE hFind;
  try {
    XMLPlatformUtils::Initialize();
  }
  catch(const XMLException& e) {
    return -1;
  }
  char szFileURL[128];
  char szFilename[128];
  char szBasePath[32];
  strcpy(szBasePath, "checkin\\response");
  while ( true ) {
    // Check for a new file every second
    switch ( WaitForSingleObject( hTermEvt, 1000) )
    {
    case WAIT_OBJECT_0:
      // The thread was told to terminate
      XMLPlatformUtils::Terminate();
      return 0;
    default:
      // Check for check-in response files
      sprintf(szFilename, "c:\\%s\\*.xml", szBasePath);
      hFind = FindFirstFile(szFilename, &findFileData);
      bool fMore = true;
      while ( fMore && hFind!=INVALID_HANDLE_VALUE ) {
        sprintf( szFileURL, "file:///checkin/response/%s", 
                 findFileData.cFileName);
        // A file exists, attempt to parse it
        static const XMLCh gLS[] 
          = {chLatin_L,chLatin_S,chNull};

        DOMImplementation* impl 
          = DOMImplementationRegistry
            ::getDOMImplementation(gLS);
        DOMBuilder* parser = 
          ((DOMImplementationLS*)impl)->createDOMBuilder(
              DOMImplementationLS::MODE_SYNCHRONOUS, 0);
        try {
          DOMCountErrorHandler errorHandler;
          parser->setErrorHandler(&errorHandler);
          // Parse the XML file and traverse the DOM
          DOMDocument* doc = parser->parseURI(X(szFileURL));
          DOMNode* elem = (DOMNode*)doc->getDocumentElement();
          if ( elem ) {
            char szLastName[32];
            char szFirstName[32];
            char szStatus[32];
            GetNodeValue(elem, "Last", szLastName);
            GetNodeValue(elem, "First", szFirstName);
            GetNodeValue(elem, "Status", szStatus);
            char message[128];
            sprintf(message, "Payment for %s %s %s", szFirstName, 
               szLastName, szStatus );
            ::MessageBox( NULL, message, "Payment Status", MB_OK);
          }
        }
        catch(DOMException& de) {
          cout << "Exception parsing document: " << de.msg;
        }
        catch (...) { }
        delete parser;
        char szFileDel[128];
        sprintf(szFileDel, "c:\\%s\\%s", szBasePath, 
           findFileData.cFileName );
        DeleteFile( szFileDel );
        fMore = FindNextFile(hFind, &findFileData);
      }
      FindClose(hFind);
      break;
    }
  }
  XMLPlatformUtils::Terminate();
  return 0;
}

Parsing XML Documents

Parsing an existing XML file in both Java and C++ is similar and straightforward. Example 4 shows the process for parsing a document in C++. First, obtain a DOMBuilder object by calling createDOMBuilder on the DOMImplementation class. Next, call the DOMBuilder.parseURI method to parse the document, passing in the filename and path in URI format. For instance, to parse the file MyDocument.xml, in the folder \myFiles, the URI is file:///myFiles/MyDocument.xml.

Example 4: Parsing an XML file in C++.

// Get a DOMImplementation object
static const XMLCh gLS[] = {chLatin_L,chLatin_S,chNull};
DOMImplementation* impl =   
    DOMImplementationRegistry
    ::getDOMImplementation(gLS);
// Create the DOMBuilder object
DOMBuilder* parser = 
    ((DOMImplementationLS*)impl)->createDOMBuilder  
        (DOMImplementationLS::MODE_SYNCHRONOUS, 0);
// Parse the file specified in URI format
DOMDocument* doc 
    = parser->parseURI("file:///myDocs/myDocument.xml");

Example 5 is the Java code for parsing an XML file. After creating a DOMParser object, the specified XML file is processed by calling the parse method. Calling DOMParser.getDocument returns the Document object.

Example 5: Parsing an XML file in Java.

// Create a DOMParser
DOMParser parser = new DOMParser();
// Parse the specified file
parser.parse("/myDocuments/myDocument.xml");
Document doc = parser.getDocument();

Traversing the DOM in both C++ and Java means iterating through the document elements recursively. Starting with the root element, each element's type is checked, and child elements are retrieved. The process continues up the tree until the elements needed are found and their values are retrieved. Example 6 shows C++ code to iterate through the DOM, searching for the XML element LastName.

Example 6: Traversing the DOM tree in C++.

// Start with the root element
DOMNode* elem = (DOMNode*)doc->getDocumentElement();
// Iterate through the child elements
DOMNode* child = elem->getFirstChild();
while ( child != NULL ) {
  // Look for the "LastName" element
  char* szElem = child->getNodeName();
  if ( _stricmp( szElem, "LastName" ) == 0 )
  {
    // Found the element, now go through its
    // children and find the text value
    DOMNodeList* childNodes = child->getChildNodes();
    for ( UINT y = 0; 
          y < childNodes->getLength(); 
          y++ ) 
    {
      DOMNode* data = childNodes->item( y );
      if ( data->getNodeType() == DOMNode::TEXT_NODE )
        return (char*)data->getNodeValue();
    }
  }
  child = child->getNextSibling();
}

The name of each child node of the root element is compared to the text "LastName". This is done by first calling DOMNode::GetFirstSibling, then GetNextSibling, in a loop. When the matching element has been found, its child nodes are retrieved and iterated through. In this case, the code is searching for the textual contents of the LastName node; therefore, it is searching for an element of type TEXT_NODE. Once found, the content is retrieved via a call to getNodeValue on the text node. The Java code to accomplish this is nearly identical; see Example 7.

Example 7: Traversing the DOM tree in Java.

// Start with the root element 
Element elem = doc.getDocumentElement();
// Iterate through the child elements
Node child = elem.getFirstChild()
while ( child != null ) {
  // Look for the "LastName" element
  if ( child.getNodeName().equalsIgnoreCase( "LastName" ) )
  {
    // Found the element, now go through its
    // children and find the text value
    NodeList childNodes = child.getChildNodes(); 
    for (int y = 0; 
         y < childNodes.getLength(); 
         y++ ) {
      Node data = childNodes.item(y);
      if ( data.getNodeType() == Node.TEXT_NODE )
        return data.getNodeValue();
    }
  }
  child = child.getNextSibling();
}
// ...

The Sample Solution

The complete source code for implementing this sample is available at http://www.cuj.com/code/. The full solution consists of a Visual C++ application that generates customer check-in requests in XML, and a Java application that processes those requests and generates XML responses.

The C++ application creates XML files and places them in the directory, c:\checkin. The Java application looks for files in this directory and places the response XML files in the directory c:\checkin\response. The C++ application parses the XML files as they appear in this directory and displays the payment status in a dialog box. This processing is done in a child thread (see the thread procedure code in Listing Two).

The C++ client application must be launched from within the c:\checkin directory; the Java application has no such restriction. Batch files have been included with the download to build and run the Java application.

Conclusion

XML serves as an excellent tool to abstract programming language and platform specifics in application development. Although there are ample tools and environments to help build XML processing code in Java, there are times when C++ is the ideal language. With the Apache XML parser for both Java and C++, you have the flexibility needed to choose the right language for the job.


Eric Bruno is a consultant in New York, and has worked extensively in Java and C++ developing real-time trading and financial applications. He can be contacted at eric ericbruno.com.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video