locked
MFC C++ XML Parse - Using MSXML. RRS feed

  • Question

  • Good day,

    I got stuck to this part of the feature I am designing can you please help?

    Here is my XML Code:

    <tables>
       <table name="Stamps.0409">
          <row index="1" >
             <column>1st Class Letter</column>
             <column>(Up to 100g)</column>
             <column>60p</column>
             <column>stampbutton1.png</column>
          </row>
          <row index="2" >
             <column>2nd Class Letter</column>
             <column>(Up to 100g)</column>
             <column>50p</column>
             <column>stampbutton2.png</column>
          </row>
       </table>
    </tables>
    


    Here is my Method:

    //log
    	_bstr_t logData;
    	_bstr_t logAttribute;
    	
    	
    	//Attribute variables
    	_variant_t vValue;
    	BSTR bstrAttributeName = ::SysAllocString(_T("row"));
    	IXMLDOMElement *pIDOMElement = NULL;
    	
    	IXMLDOMNodeList *pIDOMNodeList = NULL;
    	IXMLDOMNode *pIDOMNode = NULL;
    	
    	BSTR bstrItemText = NULL;
    	
    	long value = 0;
    	HRESULT hResult = CoInitialize(NULL);
    	if(SUCCEEDED(hResult))
    	{
    		try{
    			MSXML2::IXMLDOMDocumentPtr XMLToRead;
    			MSXML2::IXMLDOMNodeListPtr NodeListPtr;
    			MSXML2::IXMLDOMNodePtr NodeRowPtr, NodeIndexPtr, NodeTablePtr, NodeNamePtr;
    		
    			hResult = XMLToRead.CreateInstance(__uuidof(MSXML2::DOMDocument40), NULL, CLSCTX_INPROC_SERVER);
    			//TODO:PLACE LOG and TEST.
    			if(XMLToRead->load(cXMLFile) != VARIANT_TRUE)
    			{
    				trace(L6,_T("XMLDataReader: Failed to Read Data on XML"));
    			}
    		
    				trace(L6,_T("XMLDataReader: Successful Read Data on XML"));
    
    				//load xml
    				_variant_t varXml(cXMLFile);
    				_variant_t varOut((bool)TRUE);
    				varOut = XMLToRead->load(cXMLFile);
    				if((bool)varOut == FALSE)
    				throw(0);
    
    				ofstream logfile;
    				logfile.open("c:\\UKPostOfficeStampDat.log");
    				NodeListPtr =  XMLToRead->getElementsByTagName("column");
    				//Attribute
    				//Table
    				NodeTablePtr = XMLToRead->selectSingleNode("//table");
    				NodeNamePtr = NodeTablePtr->attributes->getNamedItem("name");
    				//Row
    				NodeRowPtr = XMLToRead->selectSingleNode("//row");
    				NodeIndexPtr = NodeRowPtr->attributes->getNamedItem("index");
    
    				string logAtt = _bstr_t(NodeIndexPtr->nodeValue);
    				string logTable = _bstr_t(NodeNamePtr->nodeValue);
    				
    				logfile<<logTable<<","<<logAtt<<",";
    				for(int i = 0; i < NodeListPtr->length; i++)
    				{
    					logData = NodeListPtr->Getitem(i)->text;
    					logfile<<(string)logData<<",";
    					
    				}	
    				
    				logfile.close();
    
    				trace(L6,_T("XMLDataReader: Successful Write Data on log"));
    
    			
    		}
    		catch(_com_error &e)
    		{
    			trace(L6,_T("XMLDataReader: ERROR: %ws"), e.ErrorMessage());
    		}
    		
    		CoUninitialize();
    
    	}

    This is my output code:

    Stamps.0409,1,1st Class Letter,(Up to 100g),60p,stampbutton.png,1st Class Letter,(Up to 100g),60p,stampbutton.png,

    It seems like I have only the first part of the XML data

    What I want to do is this:

    Stamps.0409,1,1st Class Letter,(Up to 100g),60p,stampbutton.png,2,2nd Class Letter,(Up to 100g),50p,stampbutton2.png,

    since there is row 2. It should have a 2 before the 2nd XML data.

    Thanks,

    Wednesday, July 17, 2013 4:52 PM

Answers

  • The function selectSingleRow(“//row”) will only return a single (first) row. Try this:

    auto table = XMLToRead->selectSingleNode("//table");

    auto rows = table->selectNodes("row");

    logfile << table->attributes->getNamedItem("name")->text << ", ";

                              

    for( int i = 0; i < rows->length; ++i )

    {

           auto row = rows->item[i];

           logfile << row->attributes->getNamedItem("index")->text << ", ";

           auto columns = row->selectNodes("column");

           for( int j = 0; j < columns->length; ++j )

           {

                  auto column = columns->item[j];

                  logfile << column->text << ", ";

           }

    }

    • Marked as answer by JosephParser Wednesday, July 17, 2013 8:21 PM
    Wednesday, July 17, 2013 6:57 PM

All replies

  • The function selectSingleRow(“//row”) will only return a single (first) row. Try this:

    auto table = XMLToRead->selectSingleNode("//table");

    auto rows = table->selectNodes("row");

    logfile << table->attributes->getNamedItem("name")->text << ", ";

                              

    for( int i = 0; i < rows->length; ++i )

    {

           auto row = rows->item[i];

           logfile << row->attributes->getNamedItem("index")->text << ", ";

           auto columns = row->selectNodes("column");

           for( int j = 0; j < columns->length; ++j )

           {

                  auto column = columns->item[j];

                  logfile << column->text << ", ";

           }

    }

    • Marked as answer by JosephParser Wednesday, July 17, 2013 8:21 PM
    Wednesday, July 17, 2013 6:57 PM
  • Thanks, Will try this today, will have a feedback on you.

    :)

    Wednesday, July 17, 2013 7:37 PM
  • Good day,

    I don't what's wrong with my dev environment, it seems it doesn't support the "auto" variable/class type. I get a conversion error. By the way I am using VC++ 6.0 and I am developing in MFC. I'm sorry I'm really new with the auto var/class type. May you please send me another w/o using auto? 

    Thanks, 

    Wednesday, July 17, 2013 7:48 PM
  • In older versions that do not support auto you have to specify the type instead of auto. Use MSXML2::IXMLDOMNodePtr for table, row and column, MSXML2::IXMLDOMNodeListPtr for rows and columns.


    • Edited by Viorel_MVP Thursday, July 18, 2013 5:53 AM add1
    Thursday, July 18, 2013 5:51 AM
  • Thanks for Helping.

    I have another question. You see I have parsed the data in the XML and placed as a single CString. I have a directive on Tables and Rows.

    ex.

    Table: Stamp.0409

    Row: 1

    I'm using this directive to be able to know that I am extracting the Table name for my getTable() method, and getRowIndex method. Do you have any idea on parsing let's say:

    Sample String: Table:Stamp.0409,Row:1,1st Class Letter . . .

    I want to create method like:

    CString getTablename(CString csParsedXMLData)
    
    {
      CString csTableName;
      
      //TODO: Extract Table name by finding the directive "Table:" to find the table name.
      return csTableName;
    }
    

    got any ideas?

    Thanks,

    Thursday, July 18, 2013 6:33 PM
  • This brings me to an exasperating point I've been encountering.

    I'm a long time programmer but a newbie to C++.  I'm using Visual Studio 2010 on Win32 XP, MSXML 6, in a MFC project.  I don't know what if any of this explains what I keep running into.

    I can take code directly off of the MSDN web site examples and run into countless compiler errors.  In the example of your reply to this thread I can use "auto" without problems.  I'm using IXMLDOMNode and IXMLDOMNodeList for clairity, however.

    Your suggestion to use IXMLDOMNodePtr is invalid for me (my compiler doesn't recognize it) so I have to use "IXMLDOMNode *row" for instance instead of "IXMLDOMNodePtr row".  I figured out the workaround for that but now the columns->length is throwing a compiler error that 'length' is not a member of IXMLDOMNodeList.

    This is just the tip.  Since I started working on this project this is all I run into.  Syntax even from MSDN that does not compile for me.  selectNodes() for instance.  My compiler says that it takes more than one parameter (your example uses only one) so I had to come up with a work around for that as well.

    My question is....why is this?  Why does the C++/MSXML syntax seem to be so fly-by-the-pants?  Is there something I'm missing when it comes to reading the references say at msdn.microsoft.com?

    Thanks

    Ed

    Tuesday, September 9, 2014 9:03 PM
  • MSXML is a very hard to use XML parser!  You might want to use another one, especially if your needs do not require a schema checking XML parser.  I like TinyXML and CMarkup.
     
    -- David
    Friday, September 12, 2014 12:53 AM
  • I'm doing fine with MSXML once I get the syntax correct.  What I keep running into are examples on the net even here on MSDN which don't compile for me because the syntax used is incompatible with my compiler.  That is what I don't understand.  I'm using VS 2010 on Win32 XP working on a MFC project.  The example given by Joseph above would not compile until I went through it line by line correcting the syntax.  Many of the problems were that he used one parameter for a function and my compiler would complain that the function took 2 parameters.  I would look it up and find that in Java Script it took one parameter and yet in C++ it took 2.  So is Joseph giving examples in Script or have all of the VS C++ syntax changed recently?  It is not just Joseph it is what I find constantly and I'm just curious as to why.  This may be common knowledge to experienced C++ users but I'm a newbie to C++ and just can't understand why the syntax jumps around so much.  It's like every machine has its own compiler and each compiler has its own syntax.  Thanks.

    Ed

    Friday, September 12, 2014 2:30 PM
  • In order to benefit from “smart pointers” (e.g. IXMLDOMNodePtr), try using #import directive, such as #import <msxml6.dll> [http://msdn.microsoft.com/en-us/library/ms757018(v=vs.85).aspx]. If you use #include <msxml6.h> instead, then smart pointers are not available automatically.

    Friday, September 12, 2014 5:08 PM
  • Thanks...this clarifies that point at least.  I've seen references to "smart pointers" but didn't know what they were referring to exactly and, of course, I've seen plenty of sample code with that "Ptr" extension which never worked for me.  I'll have to read up on "smart pointers" to find out more about them.

    Ahhh...this apparently answers my other question as well.  My observation that so much supposed C++ sample code seemed to follow the syntax of JScript and not C++.  I see in the opening page of the Smart Pointers (that you reference above) where it says that the Smart Pointer Classes "make the API calling convention in C/C++ more consistent with that in script or Visual Basic."

    OK...very slowly but surely I'm beginning to put things in place.  Like I said, I'm a newbie to C++ and this apparent inconsistent syntax was baffling.

    Thanks again.

    • Edited by EdHardin Saturday, September 13, 2014 3:34 PM
    Saturday, September 13, 2014 3:19 PM