Hi everyone,

At the minute I'm trying to learn XML and reading/parsing xml files with c++ but I seem to be a bit stuck :(

I've an xml file

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<NormalisedFileCheck>
    <ExpectedFiles>
            <File>File_Num_1</File>
            <File>File_Num_2</File>
            <File>File_Num_3</File>
            <File>File_Num_4</File>
            <File>File_Num_5</File>
    </ExpectedFiles>
    <ValidProducts>
        <Product>
            <Family>Windows</Family>
            <Name>Windows 7</Name>
        </Product>
        <Product>
            <Family>Linux</Family>
            <Name>Ubuntu</Name>
        </Product>
    </ValidProducts>
    <Parameters>
        <LogFolder>/opt/log/</LogFolder>
        <CsvFileDirectory>/opt/csv/</TaqFileDirectory>
        <DataFileDirectory>/opt/data/</DqrFileDirectory>
        <NumberOfFiles>100</NumberOfFiles>
    </Parameters>
</NormalisedFileCheck>

But I can't seem to get my program to read in anything the correct way - for example, for the <Parameters> bit, I just want it to read in each node under Parameters and display the tag name and text, so it should be say LogFolder : /opt/log

Does anyone know any good tutorials/guides for learning XML in c++? Most the ones I've come across are Java and my Java isn't the best these days (neglecting it for c++ :P)

any help would be much appreciated - I can post some code but at the minute it's very very untidy and a bit scatterbrained :(

There's a ton of good XML libraries out there, like the GNOME libxml or boost, most of them are in the repos ;)

Also, as much a PITA it can be, you could wrute your own XML-schema and validate the files against it, to ensure you don't make mistakes in the XML ;) there are many websites and some IDEs that do it, you don't need to do it yourself ;)

As for tutorials, it really depends on the library of your choice.
I didn't read much into it, but this one using boost seems legit ;)

Yeah I've been trying to use xerces-c and it's been a bit of a nightmare so far, something really simple just doesn't seem to want to work lol

Sorry for that.

Looking at some code samples and tutos I get a strong feeling of WTH, but maybe I'll be able to help you out some, just post your code so far.

#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <list>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include "parser.hpp"

using namespace xercesc;
using namespace std;

/**
 *  Constructor initializes xerces-C libraries.
 *  The XML tags and attributes which we seek are defined.
 *  The xerces-C DOM parser infrastructure is initialized.
 */

struct Product
{
    string  Family;
    string  Name;
};

GetConfig::GetConfig(int c, char* argv [])
{
    try
    {
        XMLPlatformUtils::Initialize();  // Initialize Xerces infrastructure
    }
    catch( XMLException& e )
    {
        char* message = XMLString::transcode( e.getMessage() );
        cerr << "XML toolkit initialization error: " << message << endl;
        XMLString::release( &message );
    }
    mConfigFileParser = new XercesDOMParser;
    getConfigFile (c, argv);
    if (c >= 2)
    {
        parseConfigFileParameters ();
        parseCommandLineParameters (c, argv);
    }
}

GetConfig::~GetConfig()
{
    delete mConfigFileParser;
    try
    {
        XMLPlatformUtils::Terminate(); 
    }
    catch( xercesc::XMLException& e )
    {
        char* message = xercesc::XMLString::transcode( e.getMessage() );
        cerr << "XML ttolkit teardown error: " << message << endl;
        XMLString::release( &message );
    }
}


void GetConfig::parseConfigFile(string& configFile)
    throw( std::runtime_error )
{
    struct stat fileStatus;
    int iretStat = stat(configFile.c_str(), &fileStatus);
    if( iretStat == ENOENT )
        throw ( std::runtime_error("Path file_name does not exist, or path is an empty string.") );
    else if( iretStat == ENOTDIR )
        throw ( std::runtime_error("A component of the path is not a directory."));
    else if( iretStat == ELOOP )
        throw ( std::runtime_error("Too many symbolic links encountered while traversing the path."));
    else if( iretStat == EACCES )
        throw ( std::runtime_error("Permission denied."));
    else if( iretStat == ENAMETOOLONG )
        throw ( std::runtime_error("File can not be read\n"));

    mConfigFileParser->setValidationScheme( XercesDOMParser::Val_Never );
    mConfigFileParser->setDoNamespaces( false );
    mConfigFileParser->setDoSchema( false );
    mConfigFileParser->setLoadExternalDTD( false );

    try
    {
        mConfigFileParser->parse( configFile.c_str() );
        mDocument = mConfigFileParser->getDocument();
    }
    catch( xercesc::XMLException& e )
    {
        char* message = xercesc::XMLString::transcode( e.getMessage() );
        ostringstream errBuf;
        errBuf << "Error parsing file: " << message << flush;
        XMLString::release( &message );
    }
}

/*
string GetConfig::getValue (DOMElement* e, string t)
{
    string val  = "";
    const XMLCh* tag = XMLString::transcode (t.c_str ());

    DOMNodeList* nl = e->getElementsByTagName (tag);
    cout << "getValue::nl_size=" << nl->getLength () << endl;
    if (nl != NULL && nl->getLength () > 0)
    {
        cout << "GetConfig::getValue --> Found Element" << endl;
        DOMElement* el = dynamic_cast <DOMElement*>(nl->item (0));
        const XMLCh* v = el->getFirstChild ()->getTextContent ();
        val = XMLString::transcode (v);
        cout << "GetConfig::getValue --> Returning Value: " << val << endl;
    } 

    return val;
}
*/

void GetConfig::parseCommandLineParameters(int c, char* argv []) throw(std::runtime_error)
{
    for (int i = 1; i<c;++i)
    {
        string  arg = argv[i];
        if (arg.find ("--") != string::npos)
        {
            int split = arg.find ("=");
            if (split > 0)
            {
                string  command = arg.substr (0, split);
                string  argument    = arg.substr (split + 1);
                mCommandLine.insert (pair <string, string> (command, argument));
            }
            else
            {
                string command = argv[i];
                string argument = argv[i + 1];
                mCommandLine.insert (pair <string, string> (command, argument));
            }
        }
    }
}

void GetConfig::parseConfigFileParameters() throw(std::runtime_error)
{
   try
   {
      DOMElement* elementRoot = mDocument->getDocumentElement();
      if( !elementRoot ) throw(std::runtime_error( "empty XML document" ));
    
        string s = "Parameters";
        XMLCh* node = XMLString::transcode (s.c_str ());
        DOMNodeList*    list = elementRoot->getElementsByTagName (node);
        const XMLSize_t count = list->getLength ();
        cout << "parseConfigFileParameters::list size=" << count << endl;
        for (XMLSize_t i = 0;
             i < count;
             ++i)
        {
            
        }
    }
   catch( xercesc::XMLException& e )
   {
      char* message = xercesc::XMLString::transcode( e.getMessage() );
      ostringstream errBuf;
      errBuf << "Error parsing file: " << message << flush;
      XMLString::release( &message );
   }

}


void GetConfig::getConfigFile (int c, char* argv [])
{
    string configFile;
    string configParam = "--config";

    for (int i = 1;
         i < c;
         ++i)
    {
        string arg  = argv[i];
        int pos = arg.find (configParam);
        if (arg.find (configParam) != string::npos)
        {   
            int split = arg.find ("=");
            if (split > 0)
            {
                string  command = arg.substr (0, split);
                string  argument    = arg.substr (split + 1);
            }
            else
            {
                configFile = argv[i + 1];
                break;
            }
        }
    }
    parseConfigFile (configFile);
}


int main(int argc, char* argv [])
{
   GetConfig appConfig (argc, argv);
    
   return 0;
}

That's all my code so far... basically what it's meant to do is read in the config file from XML, read in each of the elements under <Parameters> and add them to a multimap, so <LogFolder>/opt/log/</LogFolder> would go into the map as "LogFolder" "/opt/log" but I can't seem to get it too pick up the parameters at all.. it can find the Parameter element, just nothing underneath it :S

Damn you and my topology exam.

Debugging it right now. Or at least trying to.

Edited 4 Years Ago by jaskij: n/a

Ah no worries if you've something else to do thats more important, don't want you getting distracted trying to go through my dodgy code lol I'm bound to figure it out myself eventually... or lose all my hair trying, at the minute it's looking more like the later :)

TBH, xerces still gives me the feeling of WTH, maybe other lib would be better?

Anyways, do you have a header for that class?

This article has been dead for over six months. Start a new discussion instead.