After reading text entry with QXmlStreamReader getting an entry with no visible characters when there should not be any.
-
Hi All;
I'm seeing some weird behavior with the QXmlStreamReader. After i read a text entry i keep getting an entry that has no visible characters. I'm creating a small app that will allow a person to create a config file in the following format:@
<?xml version="1.0" encoding="UTF-8"?>
<DailyCheck>
<USER ID="user1">
<DateOfLastUpdate>Fri Jan 14 16:04:11 2011</DateOfLastUpdate>
<QUESTIONS>
<QUESTION ID="0">some question</QUESTION>
</QUESTIONS>
</USER>
</DailyCheck>
@it's being written using QXmlStreamWriter which works flawlessly. Here is the code that i use to read the file:
@
void DCPConfigController::ReadConfigFile()
{
mUserQuestions->clear();
QFile tFile(CONFIG_FILE);if(!tFile.open(QIODevice::ReadOnly|QIODevice::Text)) { //add code to log problem. return; } QXmlStreamReader tReader(&tFile); User* tUser = 0; while (!tReader.atEnd()) { QXmlStreamReader::TokenType tToken = tReader.readNext(); if (tToken = QXmlStreamReader::StartElement) { if (tReader.name() == USER_TAG) { QString tUserName = tReader.attributes().value(ID_TAG).toString(); tUser = ParseUser(tReader, tUserName); mUserQuestions->insert(tUserName, *tUser); } } } tFile.close();
}
User* DCPConfigController::ParseUser(QXmlStreamReader& aReader, QString aUserName)
{
aReader.readNext();
User* tUser = new User();
tUser->UserName(aUserName);
while(!(aReader.tokenType() == QXmlStreamReader::EndElement && aReader.name() == USER_TAG))
{
if(aReader.name() == LAST_UPDATE_TAG && !aReader.isWhitespace())
{
tUser->LastUpdate(GetDataString(aReader));
}
if (aReader.name() == QUESTION_AREA_TAG && !aReader.isWhitespace())
{
tUser->Questions(ParseQuestions(aReader));
}
aReader.readNext();
}
return tUser;
}QStringList DCPConfigController::ParseQuestions(QXmlStreamReader& aReader)
{
aReader.readNext();
QStringList tQuestions;
while(!(aReader.tokenType() == QXmlStreamReader::EndElement && aReader.name() == QUESTION_AREA_TAG))
{
if (aReader.name() == QUESTION_TAG && !aReader.isWhitespace())
{
tQuestions.append(GetDataString(aReader));
}
aReader.readNext();
}
return tQuestions;
}
*QString DCPConfigController::GetDataString(QXmlStreamReader& aReader)
{
if(aReader.tokenType() != QXmlStreamReader::StartElement)
{
return QString("");
}aReader.readNext(); return aReader.text().toString();
}
@However as i read the file and i read the text entry for the "DateOfLastUpdate" which is done in the parse user method, it will read the text and store it properly then move to the next piece of the XML which should be the "QUESTIONS" tag however it falls back through the same conditions as the "DateOfLastUpdate" tag, and when the data is printed to the screen the 'blank' entry shows up as "670069BA". This also happens when i try to read each of the questions. Any ideas what i could be doing wrong? I am working in VS 2008 with the Qt 4.7.1 libraries and VS addin 1.1.7
Thanks for any help.
[EDIT: code formatting, Volker]
-
Your GetDataString() method has an invalid signature. Do you return a pointer to a QString?
Also, according to the QXmlStream Bookmarks Example (in the Qt sources in examples/xml/streambookmarks) the authors use readNextStartElement() and readElementText() to extract the data. I'd suggest to try it this way.
Also, it would be helpful if you could provide us a compilable and running example to be able to reproduce the error locally.
-
Volker, Thank you so much for getting back to me. Yes that was a typo on the GetDataString() signature. I tried the two mods that you mentioned and that didn't change anything, i still get the wierd blank spaces.
here is the text of the .cpp file that reads and writes the xml:
@
#include "user.h"
#include <QXmlStreamWriter>const QString DCPConfigController::CHECK_TAG(tr("DailyCheck"));
const QString DCPConfigController::USER_TAG(tr("USER"));
const QString DCPConfigController::QUESTION_TAG(tr("QUESTION"));
const QString DCPConfigController::QUESTION_AREA_TAG(tr("QUESTIONS"));
const QString DCPConfigController::CONFIG_FILE(tr("C:/Development/build/config/DailyCheck.config"));
const QString DCPConfigController::LAST_UPDATE_TAG(tr("DateOfLastUpdate"));
const QString DCPConfigController::ID_TAG(tr("ID"));DCPConfigController::DCPConfigController(QObject *parent)
: QObject(parent), mUserQuestions(0)
{
mUserQuestions = new QMap<QString, User>();
}DCPConfigController::~DCPConfigController()
{}
QStringList DCPConfigController::GetUserList()
{
return mUserQuestions->keys();
}
QStringList DCPConfigController::GetUsersQuestions(QString aUser)
{
if(mUserQuestions->contains(aUser))
{
return mUserQuestions->value(aUser).Questions();
}
else
{
return QStringList();
}
}
void DCPConfigController::SetUsersQuestions(QString aUser, QStringList aUsersQuestions)
{
if (mUserQuestions->contains(aUser))
{
if(mUserQuestions->value(aUser).Questions() == aUsersQuestions)
{
return;
}
else
{
mUserQuestions->remove(aUser);
}
}
User tUser;
tUser.UserName(aUser);
tUser.LastUpdate(QDateTime::currentDateTime().toString());
tUser.Questions(aUsersQuestions);
mUserQuestions->insert(aUser, tUser);
}
void DCPConfigController::ReadConfigFile()
{
mUserQuestions->clear();
QFile tFile(CONFIG_FILE);if(!tFile.open(QIODevice::ReadOnly|QIODevice::Text)) { //add code to log problem. return; } QXmlStreamReader tReader(&tFile); User* tUser = 0; while (!tReader.atEnd()) { tReader.readNextStartElement(); if (tReader.tokenType() == QXmlStreamReader::StartElement) { if (tReader.name() == USER_TAG) { QString tUserName = tReader.attributes().value(ID_TAG).toString(); tUser = ParseUser(tReader, tUserName); mUserQuestions->insert(tUserName, *tUser); } } } tFile.close();
}
void DCPConfigController::WriteConfigFile()
{
QFile tFile(CONFIG_FILE);if(!tFile.open(QIODevice::WriteOnly|QIODevice::Text)) { //add code to log problem. return; }
QXmlStreamWriter tWriter(&tFile);
tWriter.setAutoFormatting(true);
tWriter.writeStartDocument();
tWriter.writeStartElement(CHECK_TAG);foreach(QString tUserName, mUserQuestions->keys()) { int tRowNum = 0; User tUser = mUserQuestions->value(tUserName); tWriter.writeStartElement(USER_TAG); tWriter.writeAttribute(ID_TAG, tUserName); tWriter.writeTextElement(LAST_UPDATE_TAG, tUser.LastUpdate()); tWriter.writeStartElement(QUESTION_AREA_TAG); foreach(QString tQuestion, tUser.Questions()) { tWriter.writeStartElement(QUESTION_TAG); tWriter.writeAttribute(ID_TAG, QString::number(tRowNum,10)); tWriter.writeCharacters(tQuestion); tWriter.writeEndElement(); //end a question tRowNum++; } tWriter.writeEndElement(); // end the users questions tWriter.writeEndElement(); //ens the user } tWriter.writeEndElement();//ends the check tWriter.writeEndDocument(); tFile.close();
}
User* DCPConfigController::ParseUser(QXmlStreamReader& aReader, QString aUserName)
{
aReader.readNextStartElement();
User* tUser = new User();
tUser->UserName(aUserName);
while(!(aReader.tokenType() == QXmlStreamReader::EndElement && aReader.name() == USER_TAG))
{
if(aReader.name() == LAST_UPDATE_TAG && !aReader.isWhitespace())
{
tUser->LastUpdate(GetDataString(aReader));
}
if (aReader.name() == QUESTION_AREA_TAG && !aReader.isWhitespace())
{
tUser->Questions(ParseQuestions(aReader));
}
aReader.readNextStartElement();
}
return tUser;
}QStringList DCPConfigController::ParseQuestions(QXmlStreamReader& aReader)
{
aReader.readNextStartElement();
QStringList tQuestions;
while(!(aReader.tokenType() == QXmlStreamReader::EndElement && aReader.name() == QUESTION_AREA_TAG))
{
if (aReader.name() == QUESTION_TAG && !aReader.isWhitespace())
{
tQuestions.append(GetDataString(aReader));
}
aReader.readNextStartElement();
}
return tQuestions;
}
QString DCPConfigController::GetDataString(QXmlStreamReader& aReader)
{
if(!aReader.isStartElement())
{
return QString("");
}// aReader.readNextStartElement();
return aReader.readElementText();
}
@Here is the user .h file. The .cpp file only has the default constructor and destructor.
@
#ifndef USER_H
#define USER_H#include <QString>
#include <QStringList>
#include <QDateTime>class User
{
public:
User();
~User();QString UserName() const { return mUserName; } void UserName(QString val) { mUserName = val; } QString LastUpdate() const { return mLastUpdate; } void LastUpdate(QString val) { mLastUpdate = val; } QStringList Questions() const { return mQuestions; } void Questions(QStringList val) { mQuestions = val; }
private:
QString mUserName;
QString mLastUpdate;
QStringList mQuestions;
};#endif // USER_H
@[EDIT: code formatting - please use @-Tags, Volker]
-
Sorry, still not a short, complete, compilable example.
- short: Leave out anything that is not necessary to reproduce the problem (eg. writing stuff, when the problem is in the reading part)
- complete + compilable: all files needed to produce an executable that demonstrates the error. This does include the necessary header files and - in Qt world - a project file, and - if used - the data files.
Nothing less. Nothing more. No platform dependend paths. Use paths relative to your executable.
Please understand, that we do not have time nor find it funny to create header files for some random class we are supposed to analyze.
Regarding your code, after a short look:
If you call readNextStartElement() you need not check the token type. It is always of type QXmlStreamReader::StartElement.Also, stick to one type of read: Either readNextStartElement() + readElementText() (and attributes() of course) or do readNext() and peek the contents manually.
You did not have a look at the mentioned streambookmarks example, did you? You can apply the techniques demonstrated there almost one-to-one to your project. You must be brave enough to wipe out your old code and start from scratch, though.