Tuesday, March 19, 2013

Read XML using C++


Using this class you can read tag and value of a XML file and add the values in to a std::list.



//ReadXML.h

#include
#include
#include
#include
#import rename_namespace(_T("MSXML"))

class CReadXML
{
public:
CReadXML();
~CReadXML();
HRESULT CreateAndInitDOM(IXMLDOMDocument **ppDoc);
HRESULT VariantFromString(PCWSTR wszValue, VARIANT &Variant);
void LoadDOM( std::string pFileName, std::list>& values );
std::wstring String2WS (const std::string& s);
std::string ConvertWCSToMBS(const wchar_t* pstr, long wslen);
std::string ConvertBSTRToString(BSTR bstr);
BOOL CheckSubNodes( IXMLDOMNode *pNode );
void ReadNodeValues( IXMLDOMNode *pNode, std::list>& values );
void ReadSubNode( IXMLDOMNode *pNode, std::list>& values );

};


//ReadXML.cpp

#include
#include "ReadXML.h"
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms765465(v=vs.85).aspx

// Macro to verify memory allcation.
#define CHK_ALLOC(p)        do { if (!(p)) { hr = E_OUTOFMEMORY; goto CleanUp; } } while(0)

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


CReadXML::CReadXML()
{

}

CReadXML::~CReadXML()
{

}

// Helper function to create a DOM instance.
HRESULT CReadXML::CreateAndInitDOM(IXMLDOMDocument **ppDoc)
{

HRESULT hr = CoCreateInstance( __uuidof(MSXML::DOMDocument30), NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)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);
}
return hr;

//NOT WORKING, IF "xmlns=" IS IN XML FILE
//HRESULT hr = CoCreateInstance(__uuidof(MSXML::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);
//   }


}

// Helper function to create a VT_BSTR variant from a null terminated string.
HRESULT CReadXML::VariantFromString(PCWSTR wszValue, VARIANT &Variant)
{
HRESULT hr = S_OK;
BSTR bstr = SysAllocString(wszValue);
CHK_ALLOC(bstr);

V_VT(&Variant)   = VT_BSTR;
V_BSTR(&Variant) = bstr;

CleanUp:
return hr;
}

std::wstring CReadXML::String2WS (const std::string& s)
{
       std::wstringstream ws;

ws << s.c_str();
std::wstring sLogLevel = ws.str();
return sLogLevel;


// std::wstring ws;
//ws.assign (s.begin (), s.end ());
// return ws;
}

std::string CReadXML::ConvertWCSToMBS(const wchar_t* pstr, long wslen)
{
    int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);

    std::string dblstr(len, '?');
    len = ::WideCharToMultiByte(CP_ACP, 0 /* no flags */,
                                pstr, wslen /* not necessary NULL-terminated */,
                                &dblstr[0], len,
                                NULL, NULL /* no default char */);

    return dblstr;
}

std::string CReadXML::ConvertBSTRToString(BSTR bstr)
{
    int wslen = ::SysStringLen(bstr);
    return ConvertWCSToMBS((wchar_t*)bstr, wslen);
}

void CReadXML::LoadDOM( std::string pFileName, std::list>& values )
{
HRESULT hr = S_OK;

IXMLDOMDocument *pXMLDom = NULL;
IXMLDOMParseError *pXMLErr = NULL;
IXMLDOMNodeList *NodeList = NULL;
IXMLDOMNode *pNode = NULL;

std::wstring wspFileName;

BSTR bstrXML = NULL;
BSTR bstrErr = NULL;
BSTR bstrQuery2 = NULL;

VARIANT_BOOL varStatus;
VARIANT varFileName;
VariantInit(&varFileName);

//Initialize COM Library:
CoInitialize(NULL);

CHK_HR(CreateAndInitDOM(&pXMLDom));  

wspFileName = String2WS(pFileName);

// XML file name to load
CHK_HR(VariantFromString( wspFileName.c_str(), varFileName));

CHK_HR(pXMLDom->load(varFileName, &varStatus));

if (varStatus == VARIANT_TRUE)
{
CHK_HR(pXMLDom->get_xml(&bstrXML));
//printf("XML DOM loaded from stocks.xml:\n%S\n", bstrXML);

bstrQuery2 = SysAllocString(L"Object"); // For find the tag Object.  Please replace this with your tag name.  If you need to read all values, then replace "Object" with " ".  But the problem is that, then each tags consider as each item, can not separate the child nodes.  For a better out put specify the tag name, where you want to start.

CHK_ALLOC(bstrQuery2);
CHK_HR(pXMLDom->getElementsByTagName(bstrQuery2, &NodeList));

if(NodeList)
{
//get the length of node-set
long length = 0;
CHK_HR(NodeList->get_length(&length));

for (long i = 0; i < length; i++)
{
if ( pNode ) pNode->Release();

CHK_HR( NodeList->get_item(i, &pNode) );

if(pNode )
{
if( CheckSubNodes( pNode ) == TRUE )
{
ReadSubNode( pNode, values );
}
else
{
ReadNodeValues( pNode, values );
}
}
}
}
}
else
{
// Failed to load xml, get last parsing error
CHK_HR(pXMLDom->get_parseError(&pXMLErr));
CHK_HR(pXMLErr->get_reason(&bstrErr));
printf("Failed to load DOM from %S. %S\n", varFileName, bstrErr);
}

CleanUp:
SAFE_RELEASE(pXMLDom);
SAFE_RELEASE(pXMLErr);
SAFE_RELEASE(NodeList);
SAFE_RELEASE(pNode);

SysFreeString(bstrXML);
SysFreeString(bstrErr);
SysFreeString(bstrQuery2);

VariantClear(&varFileName);
}

void CReadXML::ReadSubNode( IXMLDOMNode *pNode, std::list>& values )
{
HRESULT hr = S_OK;
HRESULT hResult;

IXMLDOMNode *pIAttrNode = NULL;
IXMLDOMNodeList *NodeList = NULL;
IXMLDOMNamedNodeMap *pDOMNamedNodeMap = NULL;
long length = 0;
std::pair pPair;

if( pNode )
{
CHK_HR( pNode->get_childNodes(&NodeList) );
if( NodeList )
{
CHK_HR( NodeList->get_length(&length) );

for( int i = 0; i < length; i++ )
{
if (pIAttrNode) pIAttrNode->Release();

CHK_HR( NodeList->get_item(i, &pIAttrNode));

if(pIAttrNode )
{
if( CheckSubNodes( pIAttrNode ) == TRUE )
{
ReadSubNode( pIAttrNode, values );
}
else
{
ReadNodeValues( pIAttrNode, values );
}
}
}
}
}

CleanUp:
SAFE_RELEASE(NodeList);
}

BOOL CReadXML::CheckSubNodes( IXMLDOMNode *pNode )
{
HRESULT hr = S_OK;
IXMLDOMNodeList *NodeList = NULL;
long length = 0;
VARIANT_BOOL varStatus = FALSE;

if( pNode )
{
CHK_HR( pNode->get_childNodes(&NodeList) );
if( NodeList )
{
CHK_HR( NodeList->get_length(&length) );

if( length > 0 )
varStatus = TRUE;
else
varStatus = FALSE;
}
}

CleanUp:
SAFE_RELEASE(NodeList);

return varStatus;
}

void CReadXML::ReadNodeValues( IXMLDOMNode *pNode, std::list>& values )
{
HRESULT hr = S_OK;

IXMLDOMNode* pParNode = NULL;

//Variables to store item's name, parent, text and node type:
BSTR bstrItemText, bstrItemNode, bstrNodeType;
std::pair pPair;

CHK_HR( pNode->get_nodeTypeString(&bstrNodeType));
//We process only elements (nodes of "Text" type):
BSTR temp = L"text";

if (lstrcmp((LPCTSTR)bstrNodeType, (LPCTSTR)temp)==0)
{
CHK_HR( pNode->get_parentNode( &pParNode ) );
if( pParNode )
{
CHK_HR( pParNode->get_nodeName(&bstrItemNode) );
//printf("Text: %ls\n", bstrItemNode);

CHK_HR( pNode->get_text(&bstrItemText) );
//printf("Text: %ls\n", bstrItemText);

pPair.first = ConvertBSTRToString(bstrItemNode);
pPair.second = ConvertBSTRToString(bstrItemText);
values.push_back( pPair);
}
}

CleanUp:
SAFE_RELEASE(pParNode);

}



For using this class create a C++ application and add the above files into that application.  After that create an object of the class "CReadXML" and call the function "LoadDOM".

eg:

        std::list> values;
CReadXML* pCReadXML = new CReadXML();
pCReadXML->LoadDOM("response_tunnel.xml", values);

After the function call you will get the values of XML file in the std::list values!.

Enjoy...!




No comments: