parsing xml file with qtxml c++ - c++

i'm just trying to parse an xml file using QtXml lib , so i need some help b'cause i don't understand how it's work :$
so this is my xml file config.xml :
<configuration>
<user>
<foo>Something</foo>
<bar>Something too</bar>
</user>
<dll_version>
<v1>appv1</v1>
<v2>appv2</v2>
<v3>appv3</v3>
</dll_version>
</configuration>
note : dll version it's a function that get version of an app from reg and compare it to value in xml file .
what i want to do :
Create function like : GetConfValue(QString conftype, QString confname)
that return the Config Value example : GetConfValue("user", "foo") return "Something"
thanks :)

Once in past, I also needed similar functionality. I used code as below. This may not be best solution, but i worked for me.
QString GetConfValue(QString conftype, QString confname) {
QString ret = "";
QFile file("config.xml");
if (file.open(QFile::ReadOnly | QFile::Text)) {
QXmlStreamReader reader;
reader.setDevice(&file); //(file);
reader.readNext();
while (!reader.atEnd()) {
if (reader.name() == "configuration" && reader.isStartElement()) {
reader.readNext();
while (!reader.atEnd()) {
if (reader.name() == conftype && reader.isStartElement()) {
reader.readNext();
while (!reader.atEnd()) {
if (reader.name() == confname && reader.isStartElement())
return reader.readElementText();
reader.readNext();
}
}
reader.readNext();
}
}
reader.readNext();
}
} else
qDebug() << qPrintable(file.errorString());
return ret;
}

Related

How to add non compilable configuration file into QT Project?

I have a .txt file in QT project, where I store settings parameters for my app. If I read it that way
void MainWindow::ReadApplicationSettings()
{
QFile cfgFile("config.txt");
if(!cfgFile.exists())
{
qDebug() << "Cannot find config file.";
}
else
{
ParseConfigFile(cfgFile);
SetCfg();
}
cfgFile.close();
}
And Parse it:
void MainWindow::ParseConfigFile(QFile &cfgFile)
{
QString line;
if (cfgFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&cfgFile);
while (!stream.atEnd())
{
line = stream.readLine();
std::istringstream iss(line.toStdString());
std::string id, eq, val;
bool error = false;
if (!(iss >> id))
{
error = true;
}
else if (id[0] == '#')
{
continue;
}
else if (!(iss >> eq >> val >> std::ws) || eq != "=" || iss.get() != EOF)
{
error = true;
}
if (error) { throw std::runtime_error("Parse error"); }
cfgMap[id] = std::stoi(val);
}
}
}
The file exists and when the parsing begin the content of the file is empty
The result of: line = stream.readLine(); is "".
If I add the file like a resource file and opened this way:
QFile cfgFile(":config.txt");
It's working correct, but the problem is that the config file is compiled and when you have to change some value, the project must be rebuild to take effect
I tried to compose the path like that QDir::currentPath + "/config.txt", but not working as well.
Another option is the undocumented feature "file_copies" in Qt 5.6 that you can use like this:
CONFIG += file_copies
configfiles.path = $$OUT_PWD
configfiles.files = $$PWD/config.txt
COPIES += configfiles
Found here: https://stackoverflow.com/a/54162789/6842395
If you don't like this method, that post has some more options to choose.
By the way, loooking at your ParseConfigFile() method seems that your config.txt is a collection of lines with the format: key = value, very similar to a classic INI file. Maybe you could use QSettings third constructor like this:
QSettings settings("config.txt", QSettings::IniFormat);
You can use QMAKE_EXTRA_TARGET in your profile like:
copyfile.commands += $${QMAKE_COPY} $$system_path($$PWD/config.txt) $$system_path($$DESTDIR/)
first.depends = $(first) copyfile
QMAKE_EXTRA_TARGETS += first copyfile
But ensure your $$DESTDIR is correct.

QXMLStreamReader is skipping over text elements

I am writing out an XML file with some data. Later I want to read the XML file and use the data in my program. However, when I read it back in, QXMLStreamReader seems to be skipping over some child elements.
Here is a snip from my XML file:
<?xml version="1.0" encoding="UTF-8"?>
<TEMPROOT>
<Parent1>
<C0>238.195|1401.12</C0>
<C1>795.475|1087.65</C1>
<C2>995.748|756.766</C2>
</Parent1>
<Parent2>
<left>248</left>
<right>671</right>
<Width>496</Width>
<Height>583</Height>
</Parent2>
<Parent3>
<Number>9</Number>
<Blue>4</Blue>
<Red>5</Red>
</Parent3>
</TEMPROOT>
It reads Parent1 and its children correctly. My code recognizes Parent2, but skips over its children and goes directly for Parent3 where it reads its children correctly.
Here is my code where I WRITE out the XML:
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("TEMPROOT");
xmlWriter.writeStartElement("Parent1");
xmlWriter.writeTextElement("C0", str0);
xmlWriter.writeTextElement("C1", str1);
xmlWriter.writeTextElement("C2", str2);
xmlWriter.writeEndElement();
xmlWriter.writeStartElement("Parent2");
xmlWriter.writeTextElement("left", QString::number(rectCoord[0]));
xmlWriter.writeTextElement("right", QString::number(rectCoord[1]));
xmlWriter.writeTextElement("Width", QString::number(rectCoord[2]));
xmlWriter.writeTextElement("Height", QString::number(rectCoord[3]));
xmlWriter.writeEndElement();
xmlWriter.writeStartElement("Parent3");
xmlWriter.writeTextElement("Number", strNum);
xmlWriter.writeTextElement("Blue", strBlue);
xmlWriter.writeTextElement("Red", strRed);
xmlWriter.writeEndElement();
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
Here is how I READ in the XML file:
QXmlStreamReader xmlReader(&file);
QVector<QString> Qv1;
QVector<double> QV2;
QVector<QString> Qv3;
while (!xmlReader.isEndDocument())
{
xmlReader.readNext();
QString Name = xmlReader.name().toString();
if (Name == "Parent1")
{
while (xmlReader.name().toString() != "Parent2")
{
if (xmlReader.name().toString().at(0) == "C")
{
QV1.append(xmlReader.readElementText());
}
xmlReader.readNext();
}
}
else if (Name == "Parent2")
{
while (xmlReader.name().toString() != "Parent3")
{
if (xmlReader.name().toString() == "left")
{
QV2.append(xmlReader.readElementText().toDouble());
}
else if (xmlReader.name().toString() == "right")
{
QV2.append(xmlReader.readElementText().toDouble());
}
else if (xmlReader.name().toString() == "Width")
{
QV2.append(xmlReader.readElementText().toDouble());
}
else if (xmlReader.name().toString() == "Height")
{
QV2.append(xmlReader.readElementText().toDouble());
}
xmlReader.readNext();
}
}
else if (Name == "Number")
{
Qv3.append(xmlReader.readElementText());
}
else if (Name == "Blue")
{
Qv3.append(xmlReader.readElementText());
}
else if (Name == "Red")
{
Qv3.append(xmlReader.readElementText());
}
}
The MAIN issue is that the Name variable, which is the name of the current XML element on xmlReader goes from Parent2 straight to Parent3 ignoring the children of Parent2. The children of Parent1 and Parent3 are being read in with no problems. Am I doing something wrong when I read or write the xml?

C++ Qt unable to parse JSON array correctly

I am trying to parse json with Qt but I have no success. This is the ouput that I get from the server:
[{"anni":2019},{"anni":2018},{"anni":2017}]
Where that's generated from this simple php:
header('Content-Type: application/json');
echo json_encode($data);
The $data is an array containing the values you see above. I am using this piece of code in Qt 5.11.2:
void MainWindow::showYears() {
//reply is a QNetworkReply* reply;
if (reply->error() != QNetworkReply::NoError) {
//some error managment
} else {
auto responsedata = reply->readAll();
QJsonArray years = QJsonDocument::fromJson(responsedata).array();
qDebug() << QString{responsedata};
for(const QJsonValue& y : years) {
QJsonObject obj = y.toObject();
//doing "qDebug() << r" shows that r is "" (empty!)
auto r = obj["anni"].toString();
ui->comboBoxP->addItem(r);
}
}
}
What's wrong here?
Please note that qDebug() << QString{responsedata}; prints "[{\"anni\":2019},{\"anni\":2018},{\"anni\":2017}]"
The value for your field anni is an integer. Using the member function toString will not convert it to a string representation. it will return NULL. http://doc.qt.io/qt-5/qjsonvalue.html#toString
Try with: auto r = QString::number(obj["anni"].toInt());

How to make xml parser more clear

I have an xml file built in this way:
<?xml version="1.0" encoding="UTF-8"?>
<Draw>
<Input>
<Cells>
<width>100</width>
<height>28</height>
</Cells>
<Column>custom</Column>
<Custom>
<header id="0">one</header>
<header id="1">two</header>
<header id="2">three</header>
<header id="3">four</header>
<header id="4">five</header>
</Custom>
</Input>
<Output>
<Cells>
<width>82</width>
<height>20</height>
</Cells>
<Column>upper</Column>
<Custom>
<header id="0">alfa</header>
<header id="1">beta</header>
<header id="2">gamma</header>
<header id="3">delta</header>
<header id="4">epsilon</header>
</Custom>
</Output>
</Draw>
And I’m trying to extrapolate the values ​​of the header tag: since we have two sets of header tags (Input and Output) the only working code that I managed to work for now is this:
void MainWindow::readXmlFile() {
QString target;
QFile* file = new QFile("System/Settings.xml");
/* If we can't open it, let's show an error message. */
if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) return;
QXmlStreamReader xmlReader(file);
/* We'll parse the XML until we reach end of it.*/
while(!xmlReader.atEnd() && !xmlReader.hasError()) {
QXmlStreamReader::TokenType token = xmlReader.readNext();
if(token == QXmlStreamReader::StartDocument) {
continue;
}
/* If token is StartElement, we'll see if we can read it.*/
if (token == 4) {
if (xmlReader.name() == "Input" || xmlReader.name() == "Output") {
target = xmlReader.name().toString();
while (!(xmlReader.tokenType() == QXmlStreamReader::EndElement && xmlReader.name() == target)) {
if (xmlReader.tokenType() == QXmlStreamReader::StartElement) {
qDebug() << xmlReader.name().toString();
if (xmlReader.name() == "width") {
QString num = xmlReader.readElementText();
//input->horizontalHeader()->setDefaultSectionSize(num.toInt());
}
if (xmlReader.name() == "height") {
QString num = xmlReader.readElementText();
//input->verticalHeader()->setDefaultSectionSize(num.toInt());
}
if (xmlReader.name() == "header") {
//headers->append(xmlReader.readElementText());
//qDebug() << xmlReader.readElementText();
}
//input->setHorizontalHeaderLabels(header);
}
xmlReader.readNext();
}
}
}
}
/* Error handling. */
if(xmlReader.hasError()) {
QMessageBox::critical(this,
"QXSRExample::parseXML",
xmlReader.errorString(),
QMessageBox::Ok);
}
xmlReader.clear();
}
Since this code seems very repetitive, especially from line 15 to 18, could you help me to make it a little cleaner? The examples in the web are not very explanatory.
Thanks in advance
I usually use lambda expressions. You will need to add CONFIG += c++11 to your .pro file.
Then define utilities for most repetitive patterns: for instance
/* If token is StartElement, we'll see if we can read it.*/
if (token == 4) {
auto name = [&]() { return xmlReader.name().toString(); };
auto type = [&]() { return xmlReader.tokenType(); };
auto num = [&]() { return xmlReader.readElementText().toInt(); };
if (name() == "Input" || name() == "Output") {
target = name();
while (!(type() == QXmlStreamReader::EndElement && name() == target)) {
if (type() == QXmlStreamReader::StartElement) {
qDebug() << name();
if (name() == "width") {
input->horizontalHeader()->setDefaultSectionSize(num());
}
else if (name() == "height") {
input->verticalHeader()->setDefaultSectionSize(num());
}
else if (name() == "header") {
//headers->append(xmlReader.readElementText());
//qDebug() << xmlReader.readElementText();
}
//input->setHorizontalHeaderLabels(header);
}
xmlReader.readNext();
}
}
unrelated warning: your code is leaking memory, instead of allocating with new, consider the simpler stack allocation:
QFile file("System/Settings.xml");
/* If we can't open it, let's show an error message. */
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
QXmlStreamReader xmlReader(&file);
I encountered the same problem few weeks ago, I had to read and write some xml file (arround 20/50 lines).
I started with QXmlStreamReader/Writer and finally give up and use QDomDocument.
The main difference (in terms of performance) between these two objects is QDomDocument loads all the xml file in memory. The syntax is also quite easier with QDomDoc !
See the doc for some written examples: http://qt-project.org/doc/qt-4.8/qdomdocument.html

Xerces-C XML schema validation not working

Trying to get Xerces-C to validate an XML file against a schema file but with no luck. The constructor below takes in the location of the XML file and the schema file and sets relevent member variables:
Config::Config(const std::string& schemaFile, const std::string& XMLFile)
: m_schemaFile(schemaFile),
m_XMLFile(XMLFile)
{
{
//initialize
try
{
xercesc::XMLPlatformUtils::Initialize();
xalanc::XPathEvaluator::initialize();
}
catch (xercesc::XMLException& e)
{
throw XercesInitialisationException();
}
}
{
//validate XML
xercesc::XercesDOMParser m_domParser;
if (NULL == m_domParser.loadGrammar(m_schemaFile.c_str(), xercesc::Grammar::SchemaGrammarType))
{
//schema file could not be loaded
throw SchemaLoadException();
}
ParserErrorHandler errorHandler;
m_domParser.setErrorHandler(&errorHandler);
m_domParser.setDoNamespaces(true);
m_domParser.setDoSchema(true);
m_domParser.setValidationConstraintFatal(true);
m_domParser.setValidationSchemaFullChecking(true);
m_domParser.parse(m_XMLFile.c_str());
if (NULL == m_domParser.getDocument() || NULL == m_domParser.getDocument()->getDocumentElement())
{
throw XMLLoadException();
}
if (0 == m_domParser.getErrorCount())
{
std::cout << "Number of schema validation errors: " << m_domParser.getErrorCount() << std::endl;
}
else
{
//m_validated unsuccessfully against the schema
throw SchemaValidationException();
}
}
{
//set up XPath interpreter
const xalanc::XalanDOMString m_xalanXMLFile(m_XMLFile.c_str());
const xercesc::LocalFileInputSource m_xalanInputSource(m_xalanXMLFile.c_str());
// Initialise XalanSourceTree subsystem...
xalanc::XalanSourceTreeInit sourceTreeInit;
m_liaison = std::auto_ptr<xalanc::XalanSourceTreeParserLiaison>(new xalanc::XalanSourceTreeParserLiaison(m_domSupport));
m_domSupport.setParserLiaison(m_liaison.get());
m_document = m_liaison->parseXMLStream(m_xalanInputSource);
m_prefixResolver = std::auto_ptr<xalanc::XalanDocumentPrefixResolver>(new xalanc::XalanDocumentPrefixResolver(m_document));
m_evaluator = std::auto_ptr<xalanc::XPathEvaluator>(new xalanc::XPathEvaluator);
}
}
The area of the constructor where the XML parse is set up is shown below. This is where I think the problem lies:
//validate XML
xercesc::XercesDOMParser m_domParser;
if (NULL == m_domParser.loadGrammar(m_schemaFile.c_str(), xercesc::Grammar::SchemaGrammarType))
{
//schema file could not be loaded
throw SchemaLoadException();
}
ParserErrorHandler errorHandler;
m_domParser.setErrorHandler(&errorHandler);
m_domParser.setDoNamespaces(true);
m_domParser.setDoSchema(true);
m_domParser.setValidationConstraintFatal(true);
m_domParser.setValidationSchemaFullChecking(true);
m_domParser.parse(m_XMLFile.c_str());
if (NULL == m_domParser.getDocument() || NULL == m_domParser.getDocument()->getDocumentElement())
{
throw XMLLoadException();
}
if (0 == m_domParser.getErrorCount())
{
std::cout << "Number of schema validation errors: " << m_domParser.getErrorCount() << std::endl;
}
else
{
//m_validated unsuccessfully against the schema
throw SchemaValidationException();
}
When the code is compiled and ran everything works but no validation is carried out on the XML against the schema. Which means the XML is parsed even if it does not conform to the schema. After checking I have also been able to ascertain that the errorCount value remains 0 for all schemas.
You must to reference the schema on xml root node like this:
<rootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="Schema.xsd">