XML parsing to get a value of a single node

I have never done XML parsing in VC++ so I thought I would ask this here as it would save me some time.

I call a web service and store the XML that is returned in a _bstr_t.

The XML looks like this (The carriage returns are not actually there, i just put them in for visability):

<?xml version=1.0>
<ADSearchResult>
  <User>
    <mail>someone@somewhere.ca</mail>
  </User>
</ADSearchResult>

I need to get the value in the <mail> node.  How would I do this in VC++?


Thanks,

Leo
LVL 8
Leo EikelmanDirector, IT and Business DevelopmentAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Dariusz DziaraProgrammerCommented:
The following is from MSDN (find the topic itself):

#define UNICODE

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <urlmon.h>
#include <hlink.h>
#include <dispex.h>
#include "mshtml.h"
#include "msxml.h"

#define ASSERT(x)  if(!(x)) DebugBreak()
#define CHECK_ERROR(cond, err) if (!(cond)) {pszErr=(err); goto done;}
#define SAFERELEASE(p) if (p) {(p)->Release(); p = NULL;} else ;


int _cdecl main (int argc, char **argv)
{
    PSTR pszErr = NULL;
    IXMLDocument           *pDoc = NULL;
    IStream                *pStm = NULL;
    IPersistStreamInit     *pPSI = NULL;
    CHAR                   buf[MAX_PATH];
    CHAR                   *pszURL;

    HRESULT hr;

    //
    // Check usage.
    //
    if (argc != 2)
    {
        fprintf (stderr, "Usage:   %s URL\n", argv[0]);
        fprintf (stderr, "Eg %s c:\\nt\\private\\inet\\xml\\test\\channel.cdf\n", argv[0]);
        fprintf (stderr, "or %s http://ohserv/users/julianj/msnbc.cdf\n", argv[0]);
        exit (1);
    }

    //
    // HACK if passed in a file name; expand if it doesn't look like a URL.
    //
    if (CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, argv[1], 7, "http://", 7) == CSTR_EQUAL)
    {
        pszURL = argv[1];
    }
    else
    {
        pszURL = buf;
        GetFullPathNameA(argv[1], MAX_PATH, pszURL, NULL);
    }

    hr = CoInitialize(NULL);
    ASSERT(SUCCEEDED(hr));

    //
    // Create an empty XML document.
    //
    hr = CoCreateInstance(CLSID_XMLDocument, NULL, CLSCTX_INPROC_SERVER,
                                IID_IXMLDocument, (void**)&pDoc);

    CHECK_ERROR (pDoc, "CoCreateInstance Failed");

    //
    // Synchronously create a stream on a URL.
    //
    hr = URLOpenBlockingStreamA(0, pszURL, &pStm, 0,0);    
    CHECK_ERROR(SUCCEEDED(hr) && pStm, "Couldn't open stream on URL")
    //
    // Get the IPersistStreamInit interface to the XML doc.
    //
    hr = pDoc->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
    CHECK_ERROR(SUCCEEDED(hr), "QI for IPersistStreamInit failed");

    //
    // Init the XML doc from the stream.
    //
    hr = pPSI->Load(pStm);
    //CHECK_ERROR(SUCCEEDED(hr), "Couldn't load XML doc from stream");

    if(SUCCEEDED(hr))
    {
        printf("%s : XML File is well formed \r\n",argv[0]);

    }
    else
    {
        // Print error information !
        IXMLError *pXMLError = NULL ;
        XML_ERROR xmle;

        hr = pPSI->QueryInterface(IID_IXMLError, (void **)&pXMLError);
        CHECK_ERROR(SUCCEEDED(hr), "Couldn't get IXMLError");

        ASSERT(pXMLError);

        hr = pXMLError->GetErrorInfo(&xmle);
        SAFERELEASE(pXMLError);
        CHECK_ERROR(SUCCEEDED(hr), "GetErrorInfo Failed");

        printf("%s :", argv[0]);
        wprintf(TEXT(" Error on line %d. Found %s while expecting %s\r\n"),
                            xmle._nLine,
                            xmle._pszFound,
                            xmle._pszExpected);
       
        SysFreeString(xmle._pszFound);
        SysFreeString(xmle._pszExpected);
        SysFreeString(xmle._pchBuf);
    }

done: // Clean up.
    //
    // Release any used interfaces.
    //
    SAFERELEASE(pPSI);
    SAFERELEASE(pStm);
    SAFERELEASE(pDoc);

    if (pszErr)
        fprintf (stderr, "%s, last error %d\n", pszErr, GetLastError());
    return 0;
}

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Leo EikelmanDirector, IT and Business DevelopmentAuthor Commented:
This talks about getting a Stream from a URL by the looks of it...

All I want is a simple code to parse a string that is formatted as XML and retrieve data from a node..

The above looks too complicated for such a small task.

Leo
0
Dariusz DziaraProgrammerCommented:
It doen't seem too complex for me (remove error checking) ;)

You can use:

HGLOBAL hMem;
IStream *pIStream;

CreateStreamOnHGlobal(hMem, TRUE, &pIStream); // create memory based stream

pIStream->Write(data, size, &bytes);        // write your string to memory IStream here

[...]

pIStream->Release();
0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

Leo EikelmanDirector, IT and Business DevelopmentAuthor Commented:
I would prefer a solution that uses MSXML (msxml4.dll)

Leo
0
Leo EikelmanDirector, IT and Business DevelopmentAuthor Commented:
Ok I found the solution using msxml4.dll

Thanks,

Leo
0
Dariusz DziaraProgrammerCommented:
I don't want to frighten you but it will be necessary to parse data at the end.

IXMLElement *pElem;
IXMLElementCollection *pCol;
IDispatch *pDisp;

pDoc->get_root(&pElem);

pElem->get_children(&pCol);

pCol->get_length(&nCount);

for(i=0; i<nCount; i++) {
  pCol->item(vIndx1, vIndx2, &pDisp);
  pDisp->QueryInterface(IID_XMLElement, &pElemNested);  

  // and similar things
}

pCol->Release();
pElem->Release();
0
Leo EikelmanDirector, IT and Business DevelopmentAuthor Commented:
Actually my solution was much simplier because I am only dealing with a single node that will have only one value.  It looks like this:

MSXML2::IXMLDOMDocumentPtr xmlDoc("MSXML2.DOMDocument.4.0");
      xmlDoc->async = false;
    short ret = xmlDoc->loadXML(st1);
       if ( ret)
    {
      printf("Document loaded ok.");
    }
    else
    {
       printf("load problem");
    }
   
      IXMLDOMNodePtr pNode = xmlDoc->selectSingleNode("//mail");
      printf(pNode->firstChild->text);

Thanks,

Leo
0
Dariusz DziaraProgrammerCommented:
You can simplify thing using VC++ native COM support (you will avoid remembering about ->Release() call).

instead of:
IXMLElement *pElem;

declare

IXMLElementPtr pElem;
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.