QNetworkReply 返回不完整的 XML 数据
QNetworkReply returning incomplete XML data
我正在向远程服务器发送 HTTP GET 请求。使用各种参数我定义了我感兴趣的内容。特别是我确保 output=xml
在查询中,因为它使服务器 return 回复为 XML.
我的 class HttpRetriever
与各自的 QNetworkReply
和 QNetworkAccessManager
之间有以下联系(对于 QNetworkRequest
请参阅 slotStartRequest()):
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
此处感兴趣的插槽具有以下声明:
slotFinishRequest():
void HttpRetriever::slotFinishRequest()
{
LOG(INFO) << "Finishing HTTP GET request from URL \"" << this->url.toString() << "\"";
this->reply = Q_NULLPTR;
// Reset validity of reply from a previous request
this->validReply = false;
// Skip validation if it's disabled
if (!this->validateReply)
{
LOG(WARNING) << "Validation disabled. In order to enable it see the \"validate\" and \"validationMode\" in \"settings.ini\"";
this->validReply = true;
}
else
{
// Validate reply
this->validReply = validateReply();
}
if (!this->validReply)
{
return;
}
processReply(); // Parsing
this->processingRequest = false;
}
slotReadyReadRequest():
void HttpRetriever::slotReadyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
this->bufferReply = this->reply->readAll();
}
在 slotFinishRequest()
里面我调用 processReply()
:
void HttpRetriever::processReply()
{
LOG(INFO) << "Processing reply for request \"" << this->url.toString() << "\"";
LOG(DEBUG) << QString(this->bufferReply);
// Process the XML from the reply and extract necessary data
QXmlStreamReader reader;
reader.addData(this->bufferReply);
// Read the XML reply and extract required data
// TODO
while (!reader.atEnd())
{
LOG(DEBUG) << "Reading XML element";
reader.readNextStartElement();
QXmlStreamAttributes attributes = reader.attributes();
foreach (QXmlStreamAttribute attrib, attributes)
{
LOG(DEBUG) << attrib.name();
}
}
if (reader.hasError())
{
LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
}
LOG(INFO) << "Sending data to data fusion handler";
// TODO
}
我通过以下插槽触发 HTTP get 请求:
void HttpRetriever::slotStartRequest(quint32 id)
{
if (this->processingRequest)
{
this->reply->abort();
}
this->processingRequest = false;
// The first request also instantiates the manager. If the slot is called after the instance of HafasHttpRetriever
// is moved to a new thread this will ensure proper parenting
if (!this->manager)
{
this->manager = new QNetworkAccessManager(this);
}
quint32 requestId = generateRequestId(stopId);
if (!this->url.hasQuery())
{
LOG(WARNING) << "Attempting to start request without parameters";
}
// Part of the filters applied to the request to reduce the data received (for more see slotSetRequestParams())
QUrlQuery query(this->url.query());
query.addQueryItem("input", QString::number(requestId));
// TODO Add more filters; see documentation
this->url.setQuery(query);
LOG(INFO) << "Requesting data from \"" << this->url.toString() << "\" with request ID:" << requestId;
QNetworkRequest request(this->url);
this->reply = this->manager->get(request);
// Establish connections from/to the reply and the network access manager
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
}
如你所见,到目前为止我已经为我的 class 和服务器之间的网络通信奠定了基础,我还没有开始解析 XML 回复和从中提取我需要的信息。
问题是我(非常非常经常)要么
Encountered error while parsing XML data: Start tag expected.
或
Encountered error while parsing XML data: Premature end of document
在我的 processReply()
函数中。每次我收到大量回复(几百到几千行)时都会发生这种情况。当我得到一个小的(30-40 行给予或接受)时,它永远不会发生。
所以问题显然出在我接收的数据量上,它是由 QNetworkAccessManager
组合在一起的方式(或者所有这些缓冲接收到的数据块中的任何 Qt 组件)and/or 也许是我在 class 中设置网络相关组件实例的方式。我还必须在这里做一个重要的说明,即在我的浏览器(带有 HttpRequester 附加组件的最新 Firefox)中,无论它有多大,我总是收到完整的 XML是。所以这似乎是我的应用程序独有的问题,与我系统上的网络设置无关。
因为@Marco 没有写答案...
问题是我一直在通过分配 QNetworkReply::readAll()
的结果来重写缓冲区。按照建议使用 QByteArray::append()
解决问题。
为了防止此解决方案可能出现的问题,即您不断附加收到的每个下一个回复,需要在某些时候调用 QByteArray::clear()
,例如当 finished()
发出信号。当然,在将其冲入下水道之前,需要先处理其内容。
我正在向远程服务器发送 HTTP GET 请求。使用各种参数我定义了我感兴趣的内容。特别是我确保 output=xml
在查询中,因为它使服务器 return 回复为 XML.
我的 class HttpRetriever
与各自的 QNetworkReply
和 QNetworkAccessManager
之间有以下联系(对于 QNetworkRequest
请参阅 slotStartRequest()):
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
此处感兴趣的插槽具有以下声明:
slotFinishRequest():
void HttpRetriever::slotFinishRequest()
{
LOG(INFO) << "Finishing HTTP GET request from URL \"" << this->url.toString() << "\"";
this->reply = Q_NULLPTR;
// Reset validity of reply from a previous request
this->validReply = false;
// Skip validation if it's disabled
if (!this->validateReply)
{
LOG(WARNING) << "Validation disabled. In order to enable it see the \"validate\" and \"validationMode\" in \"settings.ini\"";
this->validReply = true;
}
else
{
// Validate reply
this->validReply = validateReply();
}
if (!this->validReply)
{
return;
}
processReply(); // Parsing
this->processingRequest = false;
}
slotReadyReadRequest():
void HttpRetriever::slotReadyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
this->bufferReply = this->reply->readAll();
}
在 slotFinishRequest()
里面我调用 processReply()
:
void HttpRetriever::processReply()
{
LOG(INFO) << "Processing reply for request \"" << this->url.toString() << "\"";
LOG(DEBUG) << QString(this->bufferReply);
// Process the XML from the reply and extract necessary data
QXmlStreamReader reader;
reader.addData(this->bufferReply);
// Read the XML reply and extract required data
// TODO
while (!reader.atEnd())
{
LOG(DEBUG) << "Reading XML element";
reader.readNextStartElement();
QXmlStreamAttributes attributes = reader.attributes();
foreach (QXmlStreamAttribute attrib, attributes)
{
LOG(DEBUG) << attrib.name();
}
}
if (reader.hasError())
{
LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
}
LOG(INFO) << "Sending data to data fusion handler";
// TODO
}
我通过以下插槽触发 HTTP get 请求:
void HttpRetriever::slotStartRequest(quint32 id)
{
if (this->processingRequest)
{
this->reply->abort();
}
this->processingRequest = false;
// The first request also instantiates the manager. If the slot is called after the instance of HafasHttpRetriever
// is moved to a new thread this will ensure proper parenting
if (!this->manager)
{
this->manager = new QNetworkAccessManager(this);
}
quint32 requestId = generateRequestId(stopId);
if (!this->url.hasQuery())
{
LOG(WARNING) << "Attempting to start request without parameters";
}
// Part of the filters applied to the request to reduce the data received (for more see slotSetRequestParams())
QUrlQuery query(this->url.query());
query.addQueryItem("input", QString::number(requestId));
// TODO Add more filters; see documentation
this->url.setQuery(query);
LOG(INFO) << "Requesting data from \"" << this->url.toString() << "\" with request ID:" << requestId;
QNetworkRequest request(this->url);
this->reply = this->manager->get(request);
// Establish connections from/to the reply and the network access manager
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
}
如你所见,到目前为止我已经为我的 class 和服务器之间的网络通信奠定了基础,我还没有开始解析 XML 回复和从中提取我需要的信息。
问题是我(非常非常经常)要么
Encountered error while parsing XML data: Start tag expected.
或
Encountered error while parsing XML data: Premature end of document
在我的 processReply()
函数中。每次我收到大量回复(几百到几千行)时都会发生这种情况。当我得到一个小的(30-40 行给予或接受)时,它永远不会发生。
所以问题显然出在我接收的数据量上,它是由 QNetworkAccessManager
组合在一起的方式(或者所有这些缓冲接收到的数据块中的任何 Qt 组件)and/or 也许是我在 class 中设置网络相关组件实例的方式。我还必须在这里做一个重要的说明,即在我的浏览器(带有 HttpRequester 附加组件的最新 Firefox)中,无论它有多大,我总是收到完整的 XML是。所以这似乎是我的应用程序独有的问题,与我系统上的网络设置无关。
因为@Marco 没有写答案...
问题是我一直在通过分配 QNetworkReply::readAll()
的结果来重写缓冲区。按照建议使用 QByteArray::append()
解决问题。
为了防止此解决方案可能出现的问题,即您不断附加收到的每个下一个回复,需要在某些时候调用 QByteArray::clear()
,例如当 finished()
发出信号。当然,在将其冲入下水道之前,需要先处理其内容。