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:

  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)

// 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

  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.