Парсинг XML через HTTP на C++ с помощью Qt
Не стоит пугаться столь сложного заголовка статьи, состоящего из одних терминов. Задача чтения XML документов по протоколу HTTP встречается довольно часто. Решение, приведенное в данной статье можно использовать при написании ПО, которое активно работает с любым веб-сервисом. Например, такая программа может проводить авторизацию по логину и паролю в веб-сервисе, получение новостей в отдельном окне, возможность оставлять комментарии к записям и т.д..
Но хватит слов, приступим к реализации.
Реализация
Первое что нам понадобится - это подготовить данные к отправке методом POST. Для этого воспользуемся классом QUrl и методом addQueryItem, который формирует запрос по названии параметра и его значении.
QUrl params; params.addQueryItem("e", "dowhile@dowhile.ru"); params.addQueryItem("p", "password");
Однако данные нам нужно подготовить в формате QByteArray (этого требует метод post класса QNetworkAccessManager). Ничего страшно, переведем переменную params в строку с помощью метода toString. Однако нужно будет еще удалить первый символ, так как этот метод вернет нам строку запроса для отправки методом GET, т. е. вначале будет идти символ "?". В методе POST нам этот символ не нужен.
QByteArray data; data.append(params.toString()); data.remove(0, 1);
Для отправки запроса на веб-сервис нам понадобится класс QNetworkAccessManager. С помощью него мы сможем отправлять запросы по HTTP и принимать ответы. Для работы с ним создадим в коде функцию serverRequest().
void serverRequest(QByteArray param) { }
Не забываем прописать требуемые строчки в заголовочном файле. Для экономии места я на него ссылаться не буду.
После того, как функция создана, необходимо ее вызвать из нашего основного кода, передав соответствующие аргументы.
serverRequest(data);
Теперь в этой функции нам нужно создать объекты классов QNetworkAccessManager и QNetworkRequest.
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkRequest request;
Сразу же после объявления объектов необходимо озаботиться о получении ответа. В этом нам поможет сигнал finished, который испускает объект класса QNetworkAccessManager после окончания приема данных, и слот slotServerReply, который мы создадим позже. QNetworkReply, который передается вместе сигнала к слоту - это класс, который содержит в себе ответ и информацию о запросе.
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReply(QNetworkReply*)));
Теперь нужно подготовить запрос на сервер. С помощью метода setUrl устанавливаем адрес для нашего запроса, с помощью setHeader - тип передаваемого контента, с помощью setRawHeader - произвольный (в данном случае User-Agent) элемент в заголовке. Не забудьте вместо domain.ru подставить свой домен, на котором будете производить тест, а вместо api/test_request - адрес к api веб-сервиса.
request.setUrl(QUrl("http://domain.ru/api/test_request")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setRawHeader("User-Agent", "XMLParser 0.1");
Ниже мы делаем непосредственно сам запрос, передавая параметры методом POST. Параметры в формате QByteArray получаем в виде аргумента в нашей функции serverRequest.
manager->post(request, param);
После выполнения приведенной выше команды, нашей программе останется только ожидать сигнала finished, который указывает на завершение запроса. За обработку этого сигнала у нас отвечает слот slotServerReply. Им мы сейчас и займемся.
void slotReply(QNetworkReply *reply) { }
В этом слоте мы прочитаем все данные из объекта класса QNetworkReply в строковую переменную replyString. Поскольку ответы от сервера вряд ли будут большими, то для чтения будет используем функцию readAll.
QString replyString; replyString = reply->readAll();
Далее остается только обработать пришедший сигнал. Для этого создадим отдельную функцию serverReplyParse.
void serverReplyParse(QString reply) { }
Чтобы попасть в эту функцию после чтения ответа сервера, в слоте slotServerReply нужно вызвать функцию serverReplyParse.
serverReplyParse(replyString);
В функции serverReplyParse создадим объект QDomDocument. Это позволит нам полностью считывать в память XML данные и без всяких проблем обращаться к ним.
QDomDocument xmlDoc;
С помощью метода setContent класса QDomDocument, загрузим наши XML данные.
xmlDoc.setContent(reply);
Чтобы прочесть данные по тегу, достаточно воспользоваться методом elementsByTagName, который возвращает список элементов, относящихся к этому тегу. После этого из списка выбираем нужный элемент и переводим его в XML элемент с помощью метода toElement, из которого считываем данные с помощью метода text. Все станет понятней после приведенного ниже примера:
int userId = 0; if(xmlDoc.elementsByTagName("userId").count() > 0) userId = xmlDoc.elementsByTagName("userId").at(0).toElement().text().toInt();
Пример очень простой. Сначала мы создаем численную переменную целого типа и сразу приравниваем ее к нулю. Затем проверяем с помощью метода count количество элементов под тегом userId в полученном XML документе. Если количество элементов больше ноля (т. е. существует хотя бы один такой элемент), то мы заносим в переменную userId первое (под индексом 0) число, которое хранится под этим тегом в списке.