The source code performs the following basic steps:
pXMLDoc) to hold the XML data.pXSDDoc) to hold the XML Schema definition.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.pSCache with the schemas property of the DOM object for the XML data (pXMLDoc).pXMLDoc):
ValidateDocument function. This function then calls the validate method on pXMLDoc to validate the data set as a whole.
and/or
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
Next, we'll add the resource files to the validateDOM project.