|
|
Title |
How to create and parse a simple XML document using MSXML.dll
|
Summary |
Most developers would be able to create and even parse an XML packet using simple string manupulation. Hovever the MSXML DOM make the task trival and enable the development, manipulation and parsing of very complex XML documents. |
Contributor |
Anson Cheung and John McTainsh
|
Published |
24-May-2001 |
Last updated |
24-May-2001 |
|
|
Description.
XML is becoming a popular way to pass complex data in a mostly human readable
form. In the article we will discuss how to create an XML document then parse
the document to get particular data out of it. It is quite easy to create XML
using simple text strings but we will use the very powerful MSXML Document
Object Model. Remember, DOM is goood.
What is a XML
As MSDN would say "XML is the universal language for describing and
exchanging data". For more detail see.
For this article it is something that looks like this
UsingXmlDom.xml file.
What we need to get started
Microsoft has a COM object called the Microsoft MSXML Parser tool to create and
parse XML. To use this we need to add the following code to stdafx.
// Stdafx.h
#import "msxml.dll"
using namespace MSXML;
void CheckHR( HRESULT hr );
...
// Stdafx.cpp
// ***************************************************************************
//DESCRIPTION:
// Check the hresult and throw it in an execption if unsucessfull.
//CREATED:
// 25-5-2001, 6:49:32 PM by john@mctainsh.com
// ***************************************************************************
void CheckHR( HRESULT hr )
{
if( FAILED(hr) )
_com_issue_error(hr);
}
Because we are working with COM will also need to call CoInitialize(NULL);
and CoUninitialize(); in our code and start-up and shutdown.
Some handy functions
When building XML documents and working with COM, I have found the following
functions very handy. They can be added to the code above where they are called.
The first just converts a COM execption into a MessageBox. The second and third
are used add data to the XML document.
// **************************************************************************
// DESCRIPTION:
// Display any com error in a MessageBox.
// CREATED:
// 04-04-2001, 023:06pm by Anson Cheung
// ***************************************************************************
void ComErrorMessageBox(_com_error &e)
{
bstr_t bstrSource(e.Source());
bstr_t bstrDescription(e.Description());
CString sErr, sOutMessage;
sErr.Format( _T("Code = 0x%08lx\n"), e.Error());
sOutMessage += sErr;
sErr.Format( _T("Code meaning = %s\n"), e.ErrorMessage());
sOutMessage += sErr;
sErr.Format( _T("IErrorInfo.Source = %s\n"), (LPTSTR)bstrSource );
sOutMessage += sErr;
sErr.Format( _T("IErrorInfo.Description = %s"), (LPTSTR)bstrDescription );
sOutMessage += sErr;
AfxMessageBox( sOutMessage );
}
//Disable error The identifier string exceeded the maximum allowable length and was truncated.
#pragma warning(disable:4786)
// ***************************************************************************
//DESCRIPTION:
// Start a new XML element.
//PARAMS:
// sNodeName The new node name.
// pNode The node to add the new node to.
//RETURN:
// The Node base or the new element.
//CREATED:
// 23-5-2001, 16:27:11 by john@mctainsh.com
// ***************************************************************************
IXMLDOMNodePtr StartXmlElement( CString sNodeName, IXMLDOMNodePtr pParentNode )
{
ASSERT( pParentNode != NULL );
//NODE_ELEMENT
VARIANT v;
V_VT(&v) = VT_I4;
V_I4(&v) = 1;
//Get access to the Document (Will be NULL if it is the root)
IXMLDOMDocumentPtr pXMLDoc = pParentNode->ownerDocument;
if( pXMLDoc == NULL )
pXMLDoc = pParentNode;
//Create the element
IXMLDOMNodePtr pNodeNew = pXMLDoc->createNode(v, (bstr_t)sNodeName, _T("") );
ASSERT( pNodeNew != NULL );
//append it to the parent branch
pParentNode->appendChild( pNodeNew );
return pNodeNew;
}
// ***************************************************************************
//DESCRIPTION:
// Add Attributes to the Given Element.
//PARAMS:
// sAttribute The title of the Atttribute.
// sValue The value of the attribute.
// pNode The node to add the attributes to.
//CREATED:
// 23-5-2001, 16:27:11 by john@mctainsh.com
// ***************************************************************************
void AddXmlAttribute( CString sAttribute, CString sValue, IXMLDOMNodePtr pNode )
{
ASSERT( pNode != NULL );
// NODE_ATTRIBUTE
VARIANT v;
V_VT(&v) = VT_I4;
V_I4(&v) = 2;
//Get access to the Attributes Map
IXMLDOMNamedNodeMapPtr pCurrentAttrs = NULL;
CheckHR( pNode->get_attributes( &pCurrentAttrs) );
ASSERT( pCurrentAttrs != NULL );
//Get access to the Document (Will be NULL if it is the root)
IXMLDOMDocumentPtr pXMLDoc = pNode->ownerDocument;
if( pXMLDoc == NULL )
pXMLDoc = pNode;
//Create a new attribute in the document
IXMLDOMNodePtr pNodeNew = pXMLDoc->createNode( v, (bstr_t)sAttribute, "");
ASSERT( pNodeNew != NULL );
//Assign the text and attach to the attributes
pNodeNew->Puttext( (bstr_t)sValue );
pCurrentAttrs->setNamedItem( pNodeNew );
}
Building an XML document.
Now lets build the XML document here. Some
parts of this are optional. ie The Processing Instruction and the Comment. Note
that at then end the XML document may be accessed as a string or saved to a
file.
// ***************************************************************************
//DESCRIPTION:
// Fill and XML packet of Data.
//CREATED:
// 23-5-2001, 15:00:03 by john@mctainsh.com
// ***************************************************************************
void CDlgOutXML::OnBtnBuild()
{
try
{
//Create the XML Document
IXMLDOMDocumentPtr pXMLDoc(__uuidof(DOMDocument));
//Add the Process instruction to the top of the document, "Optional"
IXMLDOMProcessingInstructionPtr pProInstrut= pXMLDoc->createProcessingInstruction(
_T("xml"),
_T("version='1.0'") );
ASSERT( pProInstrut != NULL );
ASSERT( pXMLDoc->appendChild( pProInstrut ) != NULL );
//Add some comments to the document, "Optional"
IXMLDOMCommentPtr pComment = pXMLDoc->createComment ( _T("Some notes about people") );
ASSERT( pComment != NULL );
ASSERT( pXMLDoc->appendChild( pComment ) != NULL );
//Create the root node
IXMLDOMNodePtr pNodePeople = StartXmlElement( _T("People"), pXMLDoc );
AddXmlAttribute( _T("Range"), _T("25km"), pNodePeople );
//Create the Silly people node
IXMLDOMNodePtr pNodeSillyPeople = StartXmlElement( _T("SillyPeople"), pNodePeople );
IXMLDOMNodePtr pNodePerson = NULL;
IXMLDOMNodePtr pNodePet = NULL;
//Bill gates
pNodePerson = StartXmlElement( _T("MyPerson"), pNodeSillyPeople );
AddXmlAttribute( _T("Name"), _T("Mr Wal Mart"), pNodePerson );
pNodePerson->text = _T("This is very rich man.");
pNodePet = StartXmlElement( _T("ThePet"), pNodePerson );
AddXmlAttribute( _T("Type"), _T("Dog"), pNodePet );
AddXmlAttribute( _T("Breed"), _T("Jack Russel"), pNodePet );
pNodePet->text = _T("Fox is his name.");
//Tod stevens
pNodePerson = StartXmlElement( _T("MyPerson"), pNodeSillyPeople );
AddXmlAttribute( _T("Name"), _T("Tod"), pNodePerson );
AddXmlAttribute( _T("Age"), _T("55"), pNodePerson );
//pNodePerson->text = "Is a wally";
//Create the brilliat people node
IXMLDOMNodePtr pNodeCoolPeople = StartXmlElement( _T("BrilliantPeople"), pNodePeople );
//John McTainsh
pNodePerson = StartXmlElement( _T("MyPerson"), pNodeCoolPeople );
AddXmlAttribute( _T("Name"), _T("John McTainsh"), pNodePerson );
pNodePerson->text = _T("This is a very cool person.");
//Save the XML data to a file or a string
if( false )
bstr_t bsXml = pXMLDoc->xml;
else
CheckHR( pXMLDoc->save( _T("out.xml") ) );
}
catch(_com_error &e)
{
ComErrorMessageBox(e);
}
}
Parsing the XML document.
Building XML is only half the story the things get interesting when we need
to extract data from the XML document. In this example we will extract all the
<MyPerson> elements. We will display the text for each element and all of
its attributes. Note in this example there are three <MyPerson> elements
in separate branches of the tree and that the IXMLDOMNodeListPtr
object contains all elements.
// ***************************************************************************
//DESCRIPTION:
// Parse the packet.
//CREATED:
// 23-5-2001, 14:49:45 by john@mctainsh.com
// ***************************************************************************
void CDlgOutXML::OnBtnParse()
{
try
{
//Create the XML Document
IXMLDOMDocumentPtr pXMLDoc(__uuidof(DOMDocument));
//Load the XML from a file or a string
pXMLDoc->put_async(VARIANT_FALSE);
if( false )
{
ASSERT( pXMLDoc->load( _T("out.xml") ) );
}
else
{
ASSERT( pXMLDoc->loadXML(
_T( "<People>"
" <SillyPeople>"
" <MyPerson Name=\"Mr Wal Mart\">Rich man.</MyPerson>"
" <MyPerson Name=\"Tod\" Age=\"55\" />"
" </SillyPeople>"
" <BrilliantPeople>"
" <MyPerson Name=\"John McTainsh\">Is cool.</MyPerson> "
" </BrilliantPeople>"
"</People>") ) );
}
//Get the list of items we are looking for <MyPerson>
bstr_t bsLookFor( _T("MyPerson") );
IXMLDOMNodeListPtr pNodeList = pXMLDoc->getElementsByTagName( bsLookFor );
int nList = pNodeList->length;
TRACE( _T("Looking for = %s\n"), (LPTSTR)bsLookFor );
TRACE( _T("Found %d items\n"), nList );
//Iterate through each item found
for( int n = 0; n < nList; n++ )
{
IXMLDOMNodePtr pNode = pNodeList->item[n];
bstr_t bsNodeText = pNode->text;
TRACE( _T("***************************\n") );
TRACE( _T("Text = %s\n"), (LPTSTR)bsNodeText );
IXMLDOMNamedNodeMapPtr pAttrs = pNode->attributes;
long nAttrs = pAttrs->Getlength();
//Iterate through each Attribute found
for( long nAt = 0; nAt < nAttrs; nAt++ )
{
IXMLDOMNodePtr pAttb = pAttrs->Getitem( nAt );
bstr_t bsAttbName = pAttb->nodeName;
bstr_t bsAttbValue= pAttb->text;
TRACE( _T("Attribute = %s is %s\n"), (LPTSTR)bsAttbName ,(LPTSTR)bsAttbValue );
}
}
}
catch(_com_error &e)
{
ComErrorMessageBox(e);
}
}
|