Source: validateDOM.cpp
[This sample code uses features that were first implemented in MSXML 5.0 for Microsoft Office Applications.]
The source code performs the following basic steps:
Creates a DOM instance (
pXMLDoc
) to hold the XML data.Creates a DOM instance (
pXSDDoc
) to hold the XML Schema definition.Creates an
IXMLSchemaCollection
orIXMLSchemaCollection2
object (pSCache
). This object is also called a schema cache. The application then adds the XML Schema definition (pXSDDoc
) to thepSCache
.Associates
pSCache
with theschemas
property of the DOM object for the XML data (pXMLDoc
).Calls the following validation methods on the DOM object for XML data (
pXMLDoc
):Calls the
ValidateDocument
function. This function then calls thevalidate
method onpXMLDoc
to validate the data set as a whole.and/or
Calls the
ValidateDocumentNodes
function. This function then calls thevalidateNode(pNode)
method onpXMLDoc
to validate a node object (pNode
) selected frompXMLDoc
.
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)
// ValidateDOM.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 that releases a COM object if not NULL.
#define SAFE_RELEASE(p) do { if ((p)) { (p)->Release(); (p) = NULL; } } while(0)
// Macro to verify memory allcation.
#define CHK_ALLOC(p) do { if (!(p)) { hr = E_OUTOFMEMORY; goto CleanUp; } } 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 bstrString = SysAllocString(wszValue);
CHK_ALLOC(bstrString);
V_VT(&Variant) = VT_BSTR;
V_BSTR(&Variant) = bstrString;
CleanUp:
return hr;
}
// Helper function packaging an object into a variant:
HRESULT VariantFromObject(IUnknown *pUnk, VARIANT& varObject)
{
IDispatch *pDisp = NULL;
HRESULT hr = S_OK;
VariantInit(&varObject);
CHK_HR(pUnk->QueryInterface(IID_IDispatch, (void**)&pDisp));
varObject.pdispVal = pDisp;
varObject.vt = VT_DISPATCH;
CleanUp:
return hr;
}
// Helper function to create a DOM instance.
HRESULT CreateAndInitDOM(IXMLDOMDocument3 **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 function to display parse error.
// It returns error code of the parse error.
HRESULT ReportParseError(IXMLDOMDocument *pDoc, char *szDesc)
{
HRESULT hr = S_OK;
HRESULT hrRet = E_FAIL; // Default error code if failed to get from parse error.
IXMLDOMParseError *pXMLErr = NULL;
BSTR bstrReason = NULL;
CHK_HR(pDoc->get_parseError(&pXMLErr));
CHK_HR(pXMLErr->get_errorCode(&hrRet));
CHK_HR(pXMLErr->get_reason(&bstrReason));
printf("%s\n%S\n", szDesc, bstrReason);
CleanUp:
SAFE_RELEASE(pXMLErr);
SysFreeString(bstrReason);
return hrRet;
}
// Validate XML document as a whole.
void ValidateDocument(IXMLDOMDocument3 *pDOMDoc)
{
HRESULT hr = S_OK;
IXMLDOMParseError *pErr = NULL;
BSTR bstr = NULL;
long eCode;
CHK_HR(pDOMDoc->validate(&pErr));
CHK_HR(SUCCEEDED(pErr->get_errorCode(&eCode)));
if (eCode != 0)
{
CHK_HR(pErr->get_reason(&bstr));
printf("\tXMLDoc is not valid because\n%S\n", bstr);
}
else
{
CHK_HR(pDOMDoc->get_xml(&bstr));
printf("\tXMLDoc is validated: \n%S\n", bstr);
}
CleanUp:
SAFE_RELEASE(pErr);
SysFreeString(bstr);
}
// Validate Document nodes, node by node.
void ValidateDocumentNodes(IXMLDOMDocument3 *pDOMDoc, BSTR bstrXPath)
{
HRESULT hr = S_OK;
IXMLDOMNode * pNode = NULL;
IXMLDOMNodeList *pNodelist = NULL;
IXMLDOMParseError *pError = NULL;
BSTR bstr = NULL;
long length, eCode, i;
CHK_HR(pDOMDoc->selectNodes(bstrXPath, &pNodelist));
CHK_HR(pNodelist->get_length(&length));
for (i = 0; i < length; i++)
{
CHK_HR(pNodelist->get_item(i, &pNode));
CHK_HR(pDOMDoc->validateNode(pNode, &pError));
CHK_HR(pError->get_errorCode(&eCode));
CHK_HR(pNode->get_nodeName(&bstr));
if (eCode != 0)
{
BSTR bstrReason = NULL;
CHK_HR(pError->get_reason(&bstrReason));
printf("\t<%S> (%d) is not valid because\n%S\n", bstr, i, bstrReason);
SysFreeString(bstrReason);
}
else
{
printf("\t<%S> (%d) is a valid node\n", bstr, i);
}
SAFE_RELEASE(pError);
SAFE_RELEASE(pNode);
}
CleanUp:
SAFE_RELEASE(pError);
SAFE_RELEASE(pNode);
SAFE_RELEASE(pNodelist);
SysFreeString(bstr);
}
// Validate DOMDocument
void validateDOM()
{
IXMLDOMDocument3 *pXMLDoc = NULL;
IXMLDOMDocument3 *pXSDDoc = NULL;
IXMLDOMSchemaCollection *pSCache = NULL;
VARIANT_BOOL status;
BSTR bstr = NULL;
HRESULT hr = S_OK;
VARIANT varXMLFileName;
VariantInit(&varXMLFileName);
VARIANT varXSDFileName;
VariantInit(&varXSDFileName);
VARIANT varXSDObject;
VariantInit(&varXSDObject);
VARIANT varSchemaColObject;
VariantInit(&varSchemaColObject);
// Create a DOM and load a document from books.xml
CHK_HR(CreateAndInitDOM(&pXMLDoc));
CHK_HR(VariantFromString(L"books.xml", varXMLFileName));
CHK_HR(pXMLDoc->load(varXMLFileName, &status));
if (status != VARIANT_TRUE)
{
CHK_HR(ReportParseError(pXMLDoc, "Failed to load DOM from books.xml"));
}
// Create a Dom and load a schema from books.xsd.
CHK_HR(CreateAndInitDOM(&pXSDDoc));
CHK_HR(VariantFromString(L"books.xsd", varXSDFileName));
CHK_HR(pXSDDoc->load(varXSDFileName, &status));
if (status != VARIANT_TRUE)
{
CHK_HR(ReportParseError(pXSDDoc, "Failed to load DOM from books.xsd"));
}
// Create a schema collection object.
CHK_HR(CoCreateInstance(__uuidof(XMLSchemaCache60),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IXMLDOMSchemaCollection),
(void**)&pSCache));
// Add schema to the schema collection.
bstr = SysAllocString(L"urn:books");
CHK_ALLOC(bstr);
CHK_HR(VariantFromObject(pXSDDoc, varXSDObject));
CHK_HR(pSCache->add(bstr, varXSDObject));
// Attaching the schema to the XML document.
CHK_HR(VariantFromObject(pSCache, varSchemaColObject));
CHK_HR(pXMLDoc->putref_schemas(varSchemaColObject));
printf("Validating DOM...\n");
// Validate the document as a whole.
ValidateDocument(pXMLDoc);
printf("Validating all book nodes, '//book', one by one ...\n");
// Validate all //book nodes, node by node.
SysFreeString(bstr);
bstr = SysAllocString(L"//book");
CHK_ALLOC(bstr);
ValidateDocumentNodes(pXMLDoc, bstr);
printf("Validating all children of all book nodes, //book/*, one by one ...\n");
// Validate all //book/* nodes, node by node.
SysFreeString(bstr);
bstr = SysAllocString(L"//book/*");
CHK_ALLOC(bstr);
ValidateDocumentNodes(pXMLDoc, bstr);
CleanUp:
SAFE_RELEASE(pSCache);
SAFE_RELEASE(pXMLDoc);
SAFE_RELEASE(pXSDDoc);
VariantClear(&varXMLFileName);
VariantClear(&varXSDFileName);
VariantClear(&varXSDObject);
VariantClear(&varSchemaColObject);
SysFreeString(bstr);
}
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
validateDOM();
CoUninitialize();
}
return 0;
}
To add validateDOM.cpp to the project
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.
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.