Using QXmlStreamReader to read configuration file of key-value pairs Background

The following XML snippet contains configuration parameters for our application in key-value pair manner. (Despite using INI files for such purpose may be better, I have to use XML files.)

&lt;configuration&gt;
    &lt;parameter id="serial_port_name"&gt;ttyS0&lt;/parameter&gt;
    &lt;parameter id="serial_port_baud_rate"&gt;9600&lt;/parameter&gt;
    &lt;parameter id="check_period"&gt;1000&lt;/parameter&gt;
    &lt;parameter id="locale"&gt;hu_HU&lt;/parameter&gt;
    &lt;!-- ... --&gt;
&lt;/configuration&gt;


I would like to read the configuration parameters and store them inside a QHash&lt;QString,&nbsp;QString&gt; object with minimal overhead, therefore I've chosen the QXmlStreamReader for iterating through XML elements.

Current code

The toConfigurationMap() method of the following class iterates through the elements of XML file and when a &lt;configuration&gt; element is found, starts another iteration on the level of &lt;parameter&gt; elements to parse them.

class ConfigurationReader {
public:
    explicit ConfigurationReader(QFile&amp; input) : reader(&amp;input) {
    }

    QHash&lt;QString, QString&gt; toConfigurationMap() {
        QHash&lt;QString, QString&gt; output;

        for(auto token = reader.tokenType(); !reader.atEnd();
                token = reader.readNext()) {
            if(token != QXmlStreamReader::StartElement ||
                    reader.name() != "configuration")
                continue;

            while(reader.readNextStartElement()) {
                if(reader.name() == "parameter") {
                    QString id = reader.attributes().value("id").toString();
                    output.insert(id, reader.readElementText());
                }
                else {
                    reader.skipCurrentElement();
                }
            }
        }

        return output;
    }

private:
    QXmlStreamReader reader;
};


Goals

I would like to reduce the complexity of the code above, by somehow take apart the nested loops. In order to the toConfigurationMap() method be more cleaner, I would like to communicate to the reader that "it parses the parameters for each configuration elements" through the code.
Case for not using a class

It's not clear to me whether you need a class for what you are doing. Unless you need to be able to construct an instance of ConfigurationReader and pass it around, I would say get rid of the class. You can have a function with the simple interface

QHash&lt;QString, QString&gt; getConfigurationMap(QFile&amp; input);


I would further suggest putting the function in a namespace that is specific to your app.

namespace MyApp
{
   QHash&lt;QString, QString&gt; getConfigurationMap(QFile&amp; input);
}


Use smaller functions

Instead of one large function, divide the code into multiple functions. Think of what you are doing rather than how you are doing it. The whats usually translate to smaller functions and hows translate to loops, conditionals, branches, etc.

Given that, getConfigurationMap can be implemented as:

namespace MyApp
{
   // Implementation detail.
   void getConfigurationMap(QXmlStreamReader&amp; reader,
                            QHash&lt;QString, QString&gt;&amp; map);

   // User facing interface.
   QHash&lt;QString, QString&gt; getConfigurationMap(QFile&amp; input)
   {
      QXmlStreamReader reader(input);
      QHash&lt;QString, QString&gt; map;
      getConfigurationMap(reader, map);
      return map;
   }
}


The second overload (the one that's listed first) can be implemented as:

namespace MyApp
{
   void skipUntilConfiguration(QXmlStreamReader&amp; reader);

   void readParamters(QXmlStreamReader&amp; reader,
                      QHash&lt;QString, QString&gt;&amp; map);

   // Implementation detail.
   void getConfigurationMap(QXmlStreamReader&amp; reader,
                            QHash&lt;QString, QString&gt;&amp; map);
   {
      skipUntilConfiguration(reader);
      readParamters(reader, map):
   }

   // User facing interface.
   QHash&lt;QString, QString&gt; getConfigurationMap(QFile&amp; input)
   {
      QXmlStreamReader reader(input);
      QHash&lt;QString, QString&gt; map;
      getConfigurationMap(reader, map);
      return map;
   }
}


The code can be wrapped up by implementing the final two helper functions.

namespace MyApp
{
   void skipUntilConfiguration(QXmlStreamReader&amp; reader)
   {
      whiile ( !reader.atEnd() )
      {
         if ( reader.tokenType() == QXmlStreamReader::StartElement &amp;&amp;
              reader.name() == "configuration" )
         {
            return;
         }
      }
   }

   void readParamters(QXmlStreamReader&amp; reader,
                      QHash&lt;QString, QString&gt;&amp; map)
   {
      while ( reader.readNextStartElement() ) {
          if ( reader.name() == "parameter" ) {
              QString id = reader.attributes().value("id").toString();
              map.insert(id, reader.readElementText());
          }
          else {
              reader.skipCurrentElement();
          }
      }
   }

   void getConfigurationMap(QXmlStreamReader&amp; reader,
                            QHash&lt;QString, QString&gt;&amp; map)
   {
      skipUntilConfiguration(reader);
      readParamters(reader, map):
   }

   QHash&lt;QString, QString&gt; getConfigurationMap(QFile&amp; input)
   {
      QXmlStreamReader reader(input);
      QHash&lt;QString, QString&gt; map;
      getConfigurationMap(reader, map);
      return map;
   }
}


The above code assumes that there is only one "configuration" element in your XML file. If that assumption is not correct, the functions need to be tweaked a bit.