Microsoft XML Core Services (MSXML) 5.0 for Microsoft Office - DOM Developer's Guide

Source: validateDOM.cpp

The source code performs the following basic steps:

  1. Creates a DOM instance (pXMLDoc) to hold the XML data.
  2. Creates a DOM instance (pXSDDoc) to hold the XML Schema definition.
  3. Creates an IXMLSchemaCollection or IXMLSchemaCollection2 object (pSCache). This object is also called a schema cache. The application then adds the XML Schema definition (pXSDDoc) to the pSCache.
  4. Associates pSCache with the schemas property of the DOM object for the XML data (pXMLDoc).
  5. Calls the following validation methods on the DOM object for XML data (pXMLDoc):
    • Calls the ValidateDocument function. This function then calls the validate method on pXMLDoc to validate the data set as a whole.

      and/or

    • Calls the ValidateDocumentNodes function. This function then calls the validateNode(pNode) method on pXMLDoc to validate a node object (pNode) selected from pXMLDoc.

Checks the error returned from validate method and/or the validateNode(pNode) method, to determine if the specified XML data set is valid against the given XML Schema definition.

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

#include <stdio.h>
#include <windows.h>
#import <msxml5.dll> raw_interfaces_only
using namespace MSXML2;

// Macro that calls a COM method returning HRESULT value:
#define HRCALL(a, errmsg) \
do { \
    hr = (a); \
    if (FAILED(hr)) { \
        dprintf( "%s:%d  HRCALL Failed: %s\n  0x%.8x = %s\n", \
                __FILE__, __LINE__, errmsg, hr, #a ); \
        goto clean; \
    } \
} while (0)

// Helper function that put output in stdout and debug window
// in Visual Studio.
void dprintf( char * format, ...)
{
    static char buf[1024];
    va_list args;
    va_start( args, format );
    vsprintf( buf, format, args );
    va_end( args);
    OutputDebugStringA( buf);
    printf("%s", buf);
}

// Helper function to create a DOM instance: 
IXMLDOMDocument3 * DomFromCOM()
{
   HRESULT hr;
   IXMLDOMDocument3 *pxmldoc = NULL;

    HRCALL( CoCreateInstance(__uuidof(DOMDocument50),
                      NULL,
                      CLSCTX_INPROC_SERVER,
                      __uuidof(IXMLDOMDocument),
                      (void**)&pxmldoc),
            "Create a new DOMDocument");

    HRCALL( pxmldoc->put_async(VARIANT_FALSE),
            "should never fail");
    HRCALL( pxmldoc->put_validateOnParse(VARIANT_FALSE),
            "should never fail");
    HRCALL( pxmldoc->put_resolveExternals(VARIANT_FALSE),
            "should never fail");
    HRCALL( pxmldoc->put_preserveWhiteSpace(VARIANT_TRUE),
            "should never fail");

   return pxmldoc;
clean:
   if (pxmldoc)
    {
      pxmldoc->Release();
    }
   return NULL;
}

// Helper function packaging a BSTR into a variant:
VARIANT VariantString(BSTR str)
{
   VARIANT var;
   VariantInit(&var);
   V_BSTR(&var) = SysAllocString(str);
   V_VT(&var) = VT_BSTR;
   return var;
}

// Helper function packaging an object into a variant:
VARIANT VariantObject(IUnknown *pUnk)
{
   VARIANT var;
   HRESULT hr;
   IDispatch *pDisp=NULL;

   VariantInit(&var);
   HRCALL(pUnk->QueryInterface(IID_IDispatch,(void**)&pDisp),"");
   var.pdispVal = pDisp;
   var.vt = VT_DISPATCH;
   return var;
clean:
   VariantClear(&var);
   return var;
}

// Helper function to display parse error:
void ReportParseError(IXMLDOMDocument *pDom, char *desc) {
   IXMLDOMParseError *pXMLErr=NULL;
   BSTR bstrReason = NULL;
   HRESULT hr;
   HRCALL(pDom->get_parseError(&pXMLErr),
            "dom->get_parseError: ");
   HRCALL(pXMLErr->get_reason(&bstrReason),
            "parseError->get_reason: ");
   
   dprintf("%s %S\n",desc, bstrReason);
clean:
   if (pXMLErr) pXMLErr->Release();
   if (bstrReason) SysFreeString(bstrReason);
}

// Validate XML document as a whole.
void ValidateDocument(IXMLDOMDocument2 *pDom) {

   IXMLDOMParseError *pErr=NULL;
   BSTR bstr = NULL;
   HRESULT hr;
   long eCode;

   if (!pDom) {
      dprintf("Can't validate document. Invalid DOM\n");
      return;
   }

   dprintf("Validating DOM...\n");
   HRCALL(pDom->validate(&pErr), "");
   HRCALL(pErr->get_errorCode(&eCode), "");
   if (eCode != 0) {
      HRCALL(pErr->get_reason(&bstr), "");
      dprintf("\tXMLDoc is not valid because\n%S\n",bstr);
      SysFreeString(bstr);
      bstr = NULL;
   }
   else {
      HRCALL(pDom->get_xml(&bstr),"");
      dprintf("\tXMLDoc is validated: \n%S\n",bstr);
      SysFreeString(bstr);
      bstr = NULL;
   }
clean:
   if (pErr) pErr->Release();
   if (bstr) SysFreeString(bstr);
}

// Validate Document nodes, node by node.
void ValidateDocumentNodes(IXMLDOMDocument3 *pDom, BSTR xpath) 
{
   IXMLDOMNode * pNode = NULL;
   IXMLDOMNodeList *pNodelist = NULL;
   IXMLDOMParseError *pError = NULL;
   BSTR bstr = NULL;
   long length, eCode, i;
   HRESULT hr;

   if (!pDom) {
      dprintf("Can't validate document nodes. Invalid DOM\n");
      return;
   }

   HRCALL(pDom->selectNodes(xpath,&pNodelist),"");
   HRCALL(pNodelist->get_length(&length), "");
   for (i=0; i<length; i++) {
      HRCALL(pNodelist->get_item(i, &pNode), "");
      HRCALL(pDom->validateNode(pNode, &pError),"");
      HRCALL(pError->get_errorCode(&eCode), "");
      HRCALL(pNode->get_nodeName(&bstr), "");
      if (eCode != 0 ) {
         BSTR bstr1 = NULL;
         HRCALL(pError->get_reason(&bstr1), "");
         dprintf("\t<%S> (%d) is not valid because\n%S\n",
            bstr, i, bstr1);
         SysFreeString(bstr1);
         bstr1=NULL;
      }
      else {
         dprintf("\t<%S> (%d) is a valid node\n",bstr,i);
      }
      SysFreeString(bstr);
      bstr=NULL;
   }
clean:
   if (bstr) SysFreeString(bstr);
   if (pError) pError->Release();
   if (pNode) pNode->Release();
   if (pNodelist) pNodelist->Release();
   return;
}


int main(int argc, char* argv[])
{
   IXMLDOMDocument3         *pXMLDoc   = NULL;
   IXMLDOMDocument3         *pXSDDoc   = NULL;
   IXMLDOMSchemaCollection   *pSCache   = NULL;
   BSTR                   bstr      = NULL;
   VARIANT_BOOL status;
   VARIANT var;
   HRESULT hr;

   CoInitialize(NULL);
   VariantInit(&var);

   // Create a DOm and load a document from books.xml
   pXMLDoc = DomFromCOM();
   if (!pXMLDoc) goto clean;

   VariantClear(&var);
   var = VariantString(L"books.xml");
   HRCALL(pXMLDoc->load(var, &status), "");
   if (status!=VARIANT_TRUE) {
      ReportParseError(pXMLDoc, 
         "Failed to load DOM from books.xml");
      goto clean;
   }

   // Create a Dom and load a schema from books.xsd.
   pXSDDoc = DomFromCOM();
   if (!pXSDDoc) goto clean;
   
   VariantClear(&var);
   var = VariantString(L"books.xsd");
   HRCALL(pXSDDoc->load(var, &status), "");
   if (status!=VARIANT_TRUE) {
      ReportParseError(pXSDDoc, 
         "Failed to load DOM from books.xsd");
      goto clean;
   }

   // Create a schema collection object.
    HRCALL( CoCreateInstance(__uuidof(XMLSchemaCache50),
                      NULL,
                      CLSCTX_INPROC_SERVER,
                      __uuidof(IXMLDOMSchemaCollection),
                      (void**)&pSCache),
            "Create a new Schema collection object");

   // Add schema to the schema collection.
   bstr = SysAllocString(L"urn:books");
   VariantClear(&var);
   var = VariantObject(pXSDDoc);
   HRCALL(pSCache->add(bstr,var), "add schema");
   SysFreeString(bstr);
   bstr = NULL;
   VariantClear(&var);

   // Attaching the schema to the XML document.
   var = VariantObject(pSCache);
   HRCALL(pXMLDoc->putref_schemas(var),"");
   VariantClear(&var);

   // Validate the document as a whole.
   ValidateDocument(pXMLDoc);

   // Validate all //book nodes, node by node.
   bstr = SysAllocString(L"//book");
   ValidateDocumentNodes(pXMLDoc, bstr);
   SysFreeString(bstr);
   bstr = NULL;

   // Validate all //book/* nodes, node by node.
   bstr = SysAllocString(L"//book/*");
   ValidateDocumentNodes(pXMLDoc, bstr);
   SysFreeString(bstr);
   bstr = NULL;

clean:
   if (bstr) SysFreeString(bstr);
   if (&var) VariantClear(&var);
   if (pXMLDoc) pXMLDoc->Release();
   if (pXSDDoc) pXSDDoc->Release();
   if (pSCache) pSCache->Release();

   CoUninitialize();
   return 0;
}

To add validateDOM.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 validateDOM.cpp.
  2. Copy the C/C++ source code above, and paste it into the source file you just created.

Next, we'll add the resource files to the validateDOM project.