QXmlStreamReader not parsing xml file - c++

I'm having trouble reading the follwing Xml file:
<ArrayOfScore xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Score><GameKey>201610629</GameKey><SeasonType>1</SeasonType><Season>2016</Season><Week>6</Week><Date>2016-10-13T20:25:00</Date><AwayTeam>DEN</AwayTeam><HomeTeam>SD</HomeTeam><AwayScore>13</AwayScore><HomeScore>21</HomeScore><Channel i:nil="true"/><PointSpread>3.0</PointSpread><OverUnder>44.5</OverUnder><Quarter>F</Quarter><TimeRemaining i:nil="true"/><Possession i:nil="true"/><Down i:nil="true"/><Distance i:nil="true"/><YardLine i:nil="true"/><YardLineTerritory i:nil="true"/><RedZone i:nil="true"/><AwayScoreQuarter1>0</AwayScoreQuarter1><AwayScoreQuarter2>3</AwayScoreQuarter2><AwayScoreQuarter3>0</AwayScoreQuarter3><AwayScoreQuarter4>10</AwayScoreQuarter4><AwayScoreOvertime>0</AwayScoreOvertime><HomeScoreQuarter1>7</HomeScoreQuarter1><HomeScoreQuarter2>3</HomeScoreQuarter2><HomeScoreQuarter3>9</HomeScoreQuarter3><HomeScoreQuarter4>2</HomeScoreQuarter4><HomeScoreOvertime>0</HomeScoreOvertime><HasStarted>true</HasStarted><IsInProgress>false</IsInProgress><IsOver>true</IsOver><Has1stQuarterStarted>true</Has1stQuarterStarted><Has2ndQuarterStarted>true</Has2ndQuarterStarted><Has3rdQuarterStarted>true</Has3rdQuarterStarted><Has4thQuarterStarted>true</Has4thQuarterStarted><IsOvertime>false</IsOvertime><DownAndDistance i:nil="true"/><QuarterDescription>Final</QuarterDescription><StadiumID>14</StadiumID><LastUpdated>2016-10-17T14:27:48</LastUpdated><GeoLat>32.783188</GeoLat><GeoLong>-117.119439</GeoLong><ForecastTempLow>58</ForecastTempLow><ForecastTempHigh>76</ForecastTempHigh><ForecastDescription>Sunny</ForecastDescription><ForecastWindChill>70</ForecastWindChill><ForecastWindSpeed>14</ForecastWindSpeed><AwayTeamMoneyLine>-160</AwayTeamMoneyLine><HomeTeamMoneyLine>140</HomeTeamMoneyLine><Canceled>false</Canceled><Closed>true</Closed><LastPlay i:nil="true"/><StadiumDetails><StadiumID>14</StadiumID><Name>Qualcomm Stadium</Name><City>San Diego</City><State>CA</State><Country>USA</Country><Capacity>70561</Capacity><PlayingSurface>Grass</PlayingSurface><GeoLat>32.783188</GeoLat><GeoLong>-117.119439</GeoLong></StadiumDetails></Score></ArrayOfScore>
I use the following code to try to read the elements:
QFile testxml("./test.xml");
if(testxml.open(QIODevice::ReadOnly)) {
QXmlStreamReader XMLData(&testxml);
while(!XMLData.atEnd() && !XMLData.hasError()) {
XMLData.readNext();
qDebug() << XMLData.readElementText();
if(XMLData.isStartElement()) {
qDebug() << XMLData.name();
qDebug() << XMLData.readElementText();
}
}
}
But I only get two empty strings.
I have tried reading another Xml file which looks very similar and which is also everything in one line and I can successfully parse the elements. With this file it is somehow not working.
Does anyone have any idea what could I be doing wrong?

Related

How to parse QJsonArray?

In my QT application, there is a need to store the value of structures of the same type in a JSON dictionary.
I know about the [JSON Save Game Example article][1], I tried to figure it out for a long time, I didn’t come to anything, I also surfed the forums with the same result.
The main problem is this:
I have a JSON document:
{
"devices": [
{
"name": "some name",
"price": 2000,
"year": 2022
}
]
}
I use the following code to read the information:
//open my JSON
QFile file("path/to/myfile.json");
file.open(QIODevice::ReadOnly);
QByteArray jsonData = file.readAll();
//finding array
QJsonDocument document = QJsonDocument::fromJson(jsonData);
QJsonObject object = document.object();
QJsonArray temp_array = object["devices"].toArray();
//reading
qDebug() << temp_array[0].toObject().value("name").toString(); //returned ""
qDebug() << temp_array.size(); //returned 0
qDebug() << temp_array.empty(); //returned true
qDebug() << object.keys(); //returned QList("devices")
As I previously pointed out in a comment, trying to read the values of the "name" key returned me an empty string, the size and empty functions indicate that I'm looking at an empty array. However, the keys function indicates that my json object still contains the "devices" key.
What could be the problem?
[1]: https://doc.qt.io/qt-5/qtcore-serialization-savegame-example.html
Your code seems to be doing correct things in correct order but something goes wrong. Either your file is not found or it doesn't get opened, or it is found but parsing fails.
You would find out if you did some checks instead of just assuming things work.
Below you will find example of robust programming. The example code checks
if file exists
if file opening fails
if file parsing to JSON fails
if file is actually JSON object (and not e.g. JSON array)
if size of the JSON array named "devices" is bigger than zero
if object in JSON array contains "name" property
if "name" property type is string
(I didn't try to compile the code, but I'm sure you get the idea and figure out what went wrong. If you made this kind of code to a function you would either return or throw exception when things go wrong...)
QFile file("path/to/myfile.json");
if (!file.exists()) {
qWarning() << "File doesn't exist";
}
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Couldn't open file";
}
QByteArray jsonData = file.readAll();
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(jsonData, &parseError));
if (parseError.error != QJsonParseError::NoError) {
qWarning() << "Invalid json file. Parsing failed:" << parseError.error << parseError.errorString();
}
if (!document.isObject()) {
qWarning() << "File is not JSON object!";
}
const QJsonObject &object = document.object();
QJsonArray temp_array = object["devices"].toArray();
if (temp_array.size() < 1) {
qWarning() << "JSON array object count:" << temp_array.size();
}
QJsonObject jsonObj = temp_array[0].toObject();
if (jsonObj.contains("name")) {
if (jsonObj["name"].isString()) {
qDebug() << jsonObj["name"].toString();
} else {
qWarning() << "name" << "is not string!";
}
} else {
qWarning() << "Object in JSON array did not have" << "name";
}

Better way to use QDomDocument data as text

I am a newbie, I am creating a XML file in which I need to give CRC value to cross check on server. For this I need QDomDocument data as text. For this I am first creating XML file with fake CRC value. Then I open it with QFile and read all data. Then I split data and calculate data CRC. Now I have to rewrite whole file again. I know it is the worst idea ever to write same file twice but as I am a newbie, I don't know how to do it in better style. Here is my code:-
QDomElement docElem = doc.documentElement();
QFile xmlfile(filename);
if(!xmlfile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug("Can not open file device.");
}
xmlfile.resize(0);
QXmlStreamWriter xw;
xw.setDevice(&xmlfile); //set file to XML writer
xw.setAutoFormatting(true);
xw.setAutoFormattingIndent(4);
xw.writeStartDocument();
xw.writeStartElement(fileID); //fileID as the start element
if(docElem.hasAttributes())
{
xw.writeAttribute("xmlns:xs",docElem.attribute("xmlns:xs"));
xw.writeAttribute("xmlns",docElem.attribute("xmlns"));
}
xw.writeTextElement("Frame_Start_ID","STX");
xw.writeTextElement("Frame_Length","1234");
xw.writeTextElement("Source_Device_Id","CDIS_PIS ");
xw.writeTextElement("Destination_Device_Id","DDNS-SERVER ");
xw.writeTextElement("Frame_Code","I");
xw.writeStartElement("Frame_Data");
//inside frame data
xw.writeTextElement("File_Number","1");
xw.writeTextElement("File_Name","Event");
for(int j=0;j<logFields.count();j++)
{
xw.writeTextElement(logFields.at(j),logData.at(j));
}
xw.writeEndElement();
xw.writeTextElement("CRC","14405904");
xw.writeTextElement("Frame_End_Id","ETX");
xw.writeEndDocument();
xmlfile.flush();
xmlfile.close();
QFile xmlfyle(filename);
xmlfyle.open(QIODevice::ReadWrite | QIODevice::Text);
QString content = (QString)xmlfyle.readAll();
QStringList list1 = content.split("<CRC>");
qDebug() << "Split value = " << list1.at(0);
QByteArray crc_new = crc_o.crc_generate_modbus((unsigned char*)list1.at(0).data(),list1.at(0).size());
xmlfyle.resize(0);
QXmlStreamWriter xw_new;
xw_new.setDevice(&xmlfyle); //set file to XML writer
xw_new.setAutoFormatting(true);
xw_new.setAutoFormattingIndent(4);
xw_new.writeStartDocument();
xw_new.writeStartElement(fileID); //fileID as the start element
if(docElem.hasAttributes())
{
xw_new.writeAttribute("xmlns:xs",docElem.attribute("xmlns:xs"));
xw_new.writeAttribute("xmlns",docElem.attribute("xmlns"));
}
xw_new.writeTextElement("Frame_Start_ID","STX");
xw_new.writeTextElement("Frame_Length","1234");
xw_new.writeTextElement("Source_Device_Id","CDIS_PIS ");
xw_new.writeTextElement("Destination_Device_Id","DDNS-SERVER ");
xw_new.writeTextElement("Frame_Code","I");
xw_new.writeStartElement("Frame_Data");
xw_new.writeTextElement("File_Number","1");
xw_new.writeTextElement("File_Name","Event");
for(int j=0;j<logFields.count();j++)
{
xw_new.writeTextElement(logFields.at(j),logData.at(j));
}
xw_new.writeEndElement();
char tab[10];
sprintf(tab,"%d",crc_new.data());
xw_new.writeTextElement("CRC",QString::fromUtf8(tab));
xw_new.writeTextElement("Frame_End_Id","ETX");
xw_new.writeEndDocument();
xmlfyle.flush();
xmlfyle.close();
can anyone suggest me what could be a better way to do this.Thanks
One version of QXmlStreamWriter constructor accepts a QByteArray and writes into the array instead of an output file.
QXmlStreamWriter Class
So what you can do is; using QXmlStreamWriter, prepare data for your XML in a QByteArray, do whatever you need to do with the CRC inside this data; and when everything is done, write this QByteArray to the output file.

QXmlStreamReader (Qt5): get all sub-elements inside a tag

My XML to parse is something like this:
<posts>
<post>
<id>1234</id>
<file_url>/foo/bar</file_url>
</post>
</posts>
Now, I need to insert, for every children of post, its tag name and content in a QVariantMap, where the key is the tag, and the content (text) is the value.
Also, this is what a web API returns, so I cannot hardcode tag names (except from "post") as results may vary depending on what call is made.
My code:
QXmlStreamReader reader;
reader.addData(data); // data is a QByteArray obtained from the API call
while(reader.readNextStartElement()) {
qDebug() << reader.name();
if (reader.name() == "post") {
while(reader.readNextStartElement()) {
qDebug() << reader.name() << reader.text();
}
}
}
What happens however is that I only get "id" and nothing else. As far as I understand from the documentation, this is the expected behavior, which however isn't what I need.
What should I do to obtain only the tags between <post> and </post>?
Instead of using reader.text() you need to tell the XML reader to move to the next element by using reader.readElementText() or by using reader.skipCurrentElement() if the element isn't what you're looking for.
QXmlStreamReader reader;
reader.addData(data); // data is a QByteArray obtained from the API call
while(reader.readNextStartElement()) {
qDebug() << reader.name();
if (reader.name() == "post") {
while(reader.readNextStartElement()) {
qDebug() << reader.name() << reader.readElementText();
}
}
}
You can use the QXmlStreamWriter class and the writeCurrentToken method to get the input XML. For example the following extracts until the closing </post> text:
QXmlStreamReader reader;
reader.addData(data);
QString outerXml;
QXmlStreamWriter writer(&outerXml);
while (!reader.isEndElement()
|| reader.name() != "post")
{
writer.writeCurrentToken(mXmlReader);
reader.readNext();
}
writer.writeCurrentToken(reader);
From the Qt docs:
void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
Writes the current state of the reader. All possible valid
states are supported.
The purpose of this function is to support chained processing of XML
data.

Qt: Load HTML source code to string

I have an HTML file stored locally that I have to extract text from.
Now I managed to prompt the user for the location and display the HTML file in a QTextBrowser by pressing a button. Now from what I understand the next step would be to convert to a string to be able to search for text inside the source code.
Here's my Button_clicked method so far
void MainWindow::on_getHTMLButton_clicked()
{
QString filename = openFilenameDialog();
if (filename.isEmpty())
{
QMessageBox::information(this, tr("File Name"), "Es wurde keine gültige Datei angegeben.");
}
else
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly))
{
QMessageBox::information(0, "Info", file.errorString());
}
else
{
QTextStream in(&file);
ui->textBrowserHTML->setText(in.readAll());
}
}
}
The HTML file shows in textBrowser without any issues.
My understanding so far is that I need to create a string to search for a substring in the source code.
Now my problem is that I cannot seem to create a string object with the source of the HTML file as content.
Something like
QString string = in.readAll();
does not seem to work...
I had to learn that after having read out the stream and putting it to the ui by
ui->textBrowserHTML->setText(in.readAll());
the stream is empty – so when I attempt to to do a readAllagain it results in an empty string.

Save QNetworkReply

I would like to be able to save the QNetworkReply to a QString/QByteArray. In the examples I've seen they always saves the stream to another file.
At the moment my code looks something like this, where I get a string from the host and all I want to do is to parse it to look for the specified error code.
if(_reply->error() == QNetworkReply::UnknownContentError) {
qDebug() << _reply->readAll(); // prints out the xml message
QString test = QString(_reply->readAll());
qDebug() << test; // ""
QByteArray test2 = QByteArray(_reply->readAll());
qDebug() << test2; // ""
QRegExp rxlen("(<code>)(.*(?=</code>))");
rxlen.setMinimal(true);
int pos = rxlen.indexIn(test); // pos == -1
if(pos > -1) {
qDebug() << rxlen.cap(2); // never hit
}
}
The message is pretty small and looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<error>
<code>string-value</code>
<message>string-value</message>
</error>
So how can I load this small stream into memory, or just look for the error code?
QNetworkReply inherits from QIODevice which is a stream. After you have read something from a stream it's not there anymore. After your debug line (one with // prints out the xml message comment) there's nothing to read anymore.