Save QNetworkReply - c++

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.

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";
}

Converting JSON array to QByteArray

I have an array: [0xa,0x0b,0x0c]
This is stored in QJsonArray, I want to cover this to a QByteArray. I've been searching around for a solution and have come across several methods, this is what I have tried but its not right:
QJsonObject::iterator itrBinary = objJSON.find(clsFileThread::mscszBinary);
if ( itrBinary != objJSON.end() ) {
QJsonArray aryBinary(itrBinary->toArray());
//At this point aryBinary contains:
//10,11,12 which is correct
QJsonDocument doc(aryBinary);
QByteArray aryBytes(doc.toBinaryData());
//Now aryBytes contains:
//'q','b','j' why, how?
qDebug() << aryBinary << aryBytes;
}
After the qDebug I get:
QJsonArray([10,11,12]) "qbjs\x01\x00\x00\x00\x18\x00\x00\x00\x06\x00\x00\x00\f\x00\x00\x00J\x01\x00\x00j\x01\x00\x00\x8A\x01\x00\x00"
What I want in QBytesArray is exactly what was put into the QJsonArray, 10, 11, 12.
Thank you to "eyllanesc" for input, I would have thought there would be a built in function to do this, but here is the solution:
QJsonArray aryBinary(itrBinary->toArray());
QJsonArray::iterator itrArray = aryBinary.begin();
QByteArray aryBytes;
while( itrArray != aryBinary.end() ) {
aryBytes.append(static_cast<char>(itrArray->toInt()));
itrArray++;
}
qDebug() << aryBinary << aryBytes;

ZeroMQ sockets block

I just can't understand what i did. How it works? It works just by half, but there is identical pieces of code(almost).
I have client-server application. It sends any requests, and getting response, either list, that i will transform to vector, or string that contains HTML code. So, I will try to explain, but you are welcome to ask as much questions as you want.
1) I request file from server, by sending command "2" and path. Here is code
void Connection::requestFile(string path)
{
//string cookedPath = "./" + path;
string reply = this->sendCommand("2\n" + path);
vector<string> response = this->divideString(reply);
// set as files list we got. First is a helper, so we will not add it to files list.
if (response[0] == "directories") {
// remove "directories" entry so it will not be listed then
response.erase(response.begin());
this->files = response;
this->displayHtml = false;
} else {
//else server sent string with html
this->html = reply;
this->displayHtml = true;
}
}
displayHtml here is kinda switch that will help to determine what to do.
So in this method i've used sendCommand() method, which by my opinion is origin of all troubles.
Here it comes
string Connection::sendCommand(std::string command)
{
// send command
zmq::message_t request(command.length());
memcpy (request.data(), command.c_str(), command.length());
Connection::socket.send(request);
// get reply
zmq::message_t reply;
Connection::socket.recv(&reply);
// make string out of reply
std::string rpl = std::string(static_cast<char*>(reply.data()), reply.size());
return rpl;
}
Sorry about this formatting.
Then i use these methods here
void MainWindow::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{
// set our current folder to ./folder/ + selected file
connection->currentPath = connection->currentPath + item->text().toStdString();
// update file list. as argument we give path we just got
// std::cout << connection->currentPath << endl;
ui->listWidget->clear();
connection->requestFile(connection->currentPath);
if (connection->displayHtml == true) {
// webview->updateHtml(connection->html);
cout << "html";
} else {
this->updateFilesList(connection->files);
cout << "fileslist";
}
}
This method will be called when i double click on item in Qt widget list.
Say i've double clicked on item. It partially works fine, but it isnt prints it.
But if i close application, it prints.
So i guess problem is in blocking on 'recv' zmq function, but why it works for a half then? Maybe rewrite it anyhow? Thanks.

QXmlStreamReader not parsing xml file

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?

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.