Json QtNetworkReply to QByteArray - c++

I need to do a request in Qt/c++ to get a JSON file, and then parse it and fill my object.
The request seems good, and "it looks like" my QtNetworkReply reply is filled.
But after many attempts, I still don't understand how can I convert it into a QbyteArray (I don't even know if it's the right thing to do...), for being able to convert it into my class.
Here's my code :
QNetworkAccessManager networkManager;
QUrl url("https://api.myjson.com/bins/uvki"); //url from a free json host
QNetworkRequest request;enter code here
request.setUrl(url);
QNetworkReply* reply = networkManager.get(request);
QByteArray reponse;
if (reply == NULL)
{
std::cout << "Damn" << std::endl;
exit(2);
}
reponse = reply->readAll();
if (reponse == NULL)
{
std::cout << "i hate you" << std::endl;
exit(1000);
}
I might have done some stupid stuff, I only have 2 days of c++
Can you tell me how I can convert my "reply" into my "reponse"?

The answer provided by #MichaelBoone is correct.
In addtion, with C++11, you can simplify the code by using Qt 5's QObject::connection syntax and a lambda function
QJsonDocument document;
QNetworkReply* pReply = networkManager.get(request);
connect(reply, &QNetworkReply::finished, [=](){
// the reply will return here
QByteArray response = pReply->readAll();
document = QJsonDocument::fromBinaryData(response);
});
Qt 5's connections syntax has the advantage of compile-time verification of the connection, which is not present when using the SIGNAL and SLOT macros.

You have to connect the finished() signal from reply object, or from the NetworkManager to get the results. You will also need to make *reply a class member, or you won't be able to access it within your handler SLOT.
QNetworkReply* reply = networkManager.get(request);
connect(reply, SIGNAL(finished()), this, SLOT(YourFunctionHere()));
void YourFunctionHere(){
//handle the data
}
QNetworkReply is a non-blocking function, like most QT Network functions, it is asynchronous. By the time you are reaching your conditional if statement to check the reply, it hasn't yet received a response from the network.
As far as handling the download afterwards, you are correct in using a QByteArray.
QByteArray QIODevice::readAll()
This is an overloaded function.
Reads all available data from the device, and returns it as a
QByteArray.
From there you use QJsonDocument.
QJsonDocument QJsonDocument::fromBinaryData(const QByteArray & data,
DataValidation validation = Validate)
Creates a QJsonDocument from data.
Edit - Sorry I don't have the reputation to comment, but I feel The answer provided by TheDarkKnight lends itself better to the one-off nature of a "Reply" and is less encumbered by having to create a new slot. lambdas are just very cool, and the compile time verification is nice.

Related

QNetworkAccessManager and cookies

I'm trying to download a website (youtube) that opens after user passes the consent page (accepting cookies). So i create QNetworkRequest request and set RawHeader to ("COOKIE" , "CONSENT=YES+42"). It works fine, but only with the first attempt to download. With every next attempt i bounce against the consent page. The problem is somehow bypassed when each time i use deleteLater() on QNetworkAccessManager object. But the documentation claims "One QNetworkAccessManager instance should be enough for the whole Qt application" (also creating new instance of QNetworkAccessManager for each use eventually results with not receiving a replay and rise of processor use). So my question is how to "reset" QNetworkAccessManager so with each next use, it acts as with the first request.
My code looks like this:
Youtube::Youtube(QObject *parent) : QObject(parent)
{
manager = new QNetworkAccessManager(this);
}
void Youtube::makeRequest(QString indexCore){
QNetworkReply *reply;
QNetworkRequest request;
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReadyRead(QNetworkReply*)));
request.setRawHeader("COOKIE" , "CONSENT=YES+42" ); //works
request.setUrl(QUrl("https://" + indexCore ));
reply = manager->get(request);
}
void Youtube::slotReadyRead(QNetworkReply *replay)
{
QByteArray dataTemp = replay->readAll();
website = dataTemp.toStdString();
replay->deleteLater();
}
OK.I just had to set and empty QNetworkCookieJar.

Implementing a custom QWebEngineUrlSchemeHandler, cannot reply with a QNetworkReply

Using Qt 5.8, we are setting to implement a custom QWebEngineUrlSchemeHandler. Its behaviour should be to issue a GET query and respond with the returned content.
Our understanding is that the content is returned to the web engine through QWebEngineUrlRequestJob::reply second argument, which should derive from QIODevice. And as QNetworkReply derives from it, we expected an instance of this type to be a valid content provider.
A minimal example (not concerned with freeing dynamically allocated memory) would be:
#include <QBuffer>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QWebEngineUrlRequestJob>
#include <QWebEngineUrlSchemeHandler>
class CustomHandler : public QWebEngineUrlSchemeHandler
{
Q_OBJECT
public:
void requestStarted(QWebEngineUrlRequestJob *aRequestJob) override
{
QUrl requestedUrl("http://stackoverflow.com/");
QNetworkRequest *request = new QNetworkRequest(requestedUrl);
QNetworkReply *reply = mManager.get(*request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
// Reply segment
aRequestJob->reply("text/html", reply);
}
public slots:
void slotError(QNetworkReply::NetworkError aError)
{
std::cout << "Error: " << aError << std::endl;
}
private:
QNetworkAccessManager mManager;
};
When executed, this outputs:
Error: 5
The code thus fails by calling the error slot CustomHandler::slotError with QNetworkReply::NetworkError, which corresponds to a canceled operation.
Is there a way to reply with a QNetworkReply, or is it mandatory to wait for the request to complete and then extract its reply content into a QBuffer first?
Theoretically it should work the way you coded it, but in practice it worked reliably for me only when aRequestJob->reply("text/html", reply); was called from the QNetworkReply::finished signal. You can also try to use readyRead signal, then you have to call reply every time you receive the signal, but as far as I remember it wasn't very reliable, so I decided to stick with the finished signal.
You can also move the data to QBuffer upon finished, but for any reason you have to close() the buffer before replying with it, otherwise it is not get read and appears as an empty response.
I reported this QTBUG-106461 5 days ago. The fixes are already merged into Qt dev branch and cherry-picked into several release branches. The example code in this question description should work in Qt WebEngine 6.4.1 or later.
It is unfortunate that developers choose to discuss bugs on StackOverflow and forums, apply workarounds and forget, instead of reporting them upstream. This bug could have been fixed long ago.
to avoid this error you have to insert
reply.deleteLater()
But in my case I don't see any result displayed. Don't know what to do get this working with QNetworkReply

How can I read content (http response body) from a QNetworkReply

I'm using qt5.3 and I googled a lot before I post.
I want to read data from QNetworkReply. I have a QWebView and I also need the http response to be read by QWebView to display the webpage. What I need is just to log the web content or whatever response to http posts.
The problem is QNetworkReply is something that can only be read once.
If I call readAll() when I pick readyRead() signal, I will get the full data. But it will be cleared so QWebView displays nothing (it won't get any reply data).
Or if I pick finished() signal, since the data is already read by QWebView (or QNetworkAccessManager), I get nothing if I call readAll() here. Is there somewhere that QNetworkReply, or manager or any class, stores the data which I can still read?
In #1 I can get part of the data if I call peek(). This function does not clear the response data. But it won't work if the response body is big. The QNetworkReply is a sequential thing that I can neither know its data nor read further than buffered.
I have no idea of how to do with this.....
I just want to monitor and log the request and response body of any request made on my QWebView ...
Edit: note that my data to read from response is as large as 1MB so it won't be ok to peek the whole data without further reading.
You can create your own subclass of QNetworkAccessManager and override virtual function createRequest. Call base class implementation to get the response object and connect readyRead signal to some slot that will capture the data. In that slot call peek function to read data so that WebKit will get the data also :
class NetworkAccessManagerProxy : public QNetworkAccessManager {
Q_OBJECT
signals:
void dataGot(QByteArray data);
public:
NetworkAccessManagerProxy(QObject * parent = 0)
: QNetworkAccessManager()
{
}
virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice *outgoingData)
{
reply = QNetworkAccessManager::createRequest(op, request, outgoingData);
connect(this,SIGNAL(readyRead()), SLOT(readInternal()));
return reply;
}
private slots:
void readInternal()
{
QByteArray data = reply->peek(reply->bytesAvailable());
emit dataGot(data);
}
private:
QNetworkReply* reply;
};
After creating QWebPage object, call setNetworkAccessManager and pass a newly created instance of your subclass :
QWebPage * page = new QWebPage;
page->setNetworkAccessManager(new NetworkAccessManagerProxy());
page->mainFrame()->load(url);
webView->setPage(page);

Web service in blackberry 10

I am developing a BlackBerry 10 apps with Cascades (C++ programming language) right now. Can anyone tell me how do i make a call to web service in BlackBerry 10: Cascades? I'm just a beginner, so i don't really know anything. Thanks for your answer
void GetWeb::start(const QString &str)
{
QNetworkRequest request = QNetworkRequest();
request.setUrl(QUrl(str));
QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(this);
connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
networkAccessManager->get(request);
}
void GetWeb::requestFinished(QNetworkReply* reply)
{
if (reply->error() == QNetworkReply::NoError)
{
emit complete(reply->readAll());
}
reply->deleteLater();
}
In this case I am emiting the resulting string as a signal, but you could also just use the reply->readAll() string directly if you wished...
There's a few moving parts to sending a network request using Qt. Here's the example Qt uses:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
So what you do is create a QNetworkAccessManager object, which handles the actual process of sending the request and processing the response. You then connect the signal that the manager emits when the QNetworkRequest has finished to a slot you've created called replyFinished which takes QNetworkReply * as a parameter, that might look like this:
void MyClass::replyFinished(QNetworkReply *serverResponse)
{
//do something with the response
}
You then use the managers get method to pass your QNetworkRequest, which you can create like it has been there, or separately. And that's about it, that's a minimal example that'll send a HTTP request to http://qt-project.org and return a response containing the data from the page, you can extend out from there to do things like get JSON or XML.
Example from: QtNetwork documentation

Writing a QNetworkReply to a file

I'm downloading a file using QNetworkAccessManager::get but unlike QHttp::get there's no built-in way to directly write the response to a different QIODevice.
The easiest way would be to do something like this:
QIODevice* device;
QNetworkReply* reply = manager.get(url);
connect(reply, SIGNAL(readyRead()), this, SLOT(newData()));
and then in newData slot:
device->write(reply->readAll());
But I'm not sure if this is the right way, maybe I missed something.
That looks correct. I would use the lower-level forms of read() and write(), not the QByteArray ones, which do not properly support error handling, but other than that, it looks fine.
Are you having problems with it?
Better use the finished signal to read all the contents at the end of the download process. An example (remove the event loop and use a new slot to make it asynchronous):
QNetworkAccessManager manager;
QEventLoop loop;
QNetworkReply *reply = manager.get( request );
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QFile file( "YOUR FILE" );
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
delete reply;