Как обработать XML с помощью XmlLite, возвращаемого casablanca (PPL) http_client?
Я хочу сделать запрос к веб-службе, получить содержимое XML и проанализировать его, чтобы получить конкретные значения, возвращаемые службой.
Код должен быть написан на родном C++11 (MS Visual Studio 2013). Библиотека Cassablanca PPL была выбрана. Для разбора XML был выбран XmlLite.
Я привык к программированию на C++; Тем не менее, программирование асинхронных задач из библиотеки PPL - этот подход - является новым для меня. Я знаю, что такое асинхронное программирование, и я знаю принципы параллельного программирования. Однако я не привык использовать продолжения (.then(...)
), и я только медленно оборачиваюсь вокруг концепции.
Пока что я изменил примеры, чтобы получить результат XML и записать его в текстовый файл:
// Open a stream to the file to write the HTTP response body into.
auto fileBuffer = std::make_shared<concurrency::streams::streambuf<uint8_t>>();
file_buffer<uint8_t>::open(L"test.xml", std::ios::out)
.then([=](concurrency::streams::streambuf<uint8_t> outFile) -> pplx::task < http_response >
{
*fileBuffer = outFile;
// Create an HTTP request.
// Encode the URI query since it could contain special characters like spaces.
// Create http_client to send the request.
http_client client(L"http://api4.mapy.cz/");
// Build request URI and start the request.
uri_builder builder(L"/geocode");
builder.append_query(L"query", address);
return client.request(methods::GET, builder.to_string());
})
// Write the response body into the file buffer.
.then([=](http_response response) -> pplx::task<size_t>
{
printf("Response status code %u returned.\n", response.status_code());
return response.body().read_to_end(*fileBuffer);
})
// Close the file buffer.
.then([=](size_t)
{
return fileBuffer->close();
})
// Wait for the entire response body to be written into the file.
.wait();
Теперь мне нужно понять, как изменить код, чтобы получить результат, который может быть использован XmlLite (реализация Microsoft, которая поставляется как xmllite.h
, xmllite.lib
, а также xmllite.dll
, Я знаю, что такое парсеры тяги. Но опять же, я очень новичок в библиотеке. Я все еще немного растерялся в связанных с PPL потоках и других классах. Я не знаю, как правильно их использовать. Любое объяснение приветствуется.
Люди из Кассабланки говорят, что они используют XmlLite с Кассабланкой для обработки результатов, но я не нашел ни одного примера. Можете ли вы указать мне на некоторые? Благодарю.
Обновление (4 июня 2014 г.): приведенный выше код фактически обернут как функция (wxString
исходит от wxWidgets, но его легко заменить на std::string
или же std::wstring
):
std::pair<double, double> getGeoCoordinatesFor(const wxString & address)
{
...the above code...
...here should be the XML parsing code...
return {longitude, latitude};
}
Цель на самом деле вместо записи потока в test.xml
файл для подачи анализатора XmlLite. XML довольно маленький и содержит один или несколько (если адрес неоднозначный) элементов item с атрибутами x и y, которые я хочу извлечь - например, так:
<?xml version="1.0" encoding="utf-8"?>
<result>
<point query="Vítězství 27, Olomouc">
<item
x="17.334045"
y="49.619723"
id="9025034"
source="addr"
title="Vítězství 293/27, Olomouc, okres Olomouc, Česká republika"
/>
<item
x="17.333067"
y="49.61618"
id="9024797"
source="addr"
title="Vítězství 27/1, Olomouc, okres Olomouc, Česká republika"
/>
</point>
</result>
Мне это не нужно test.xml
файл. Как получить поток и как перенаправить его в анализатор XmlLite?
1 ответ
Я еще не использовал Касабланку, так что это может быть немного не так. (Я бы хотел поработать с Касабланкой, но сначала мне придется собрать больше времени.) Тем не менее, похоже, что код, который вы показываете, загрузит файл XML и сохранит его в локальном файле. test.xml
, С этого момента легко загрузить файл в XmlLite, если XML-файл закодирован в UTF-8. Если это не UTF-8, вам придется перепрыгнуть через несколько дополнительных циклов, чтобы декодировать его, либо в памяти, либо через CreateXmlReaderInputWithEncodingName
или же CreateXmlReaderInputWithCodePage
и я не буду освещать это здесь.
Когда у вас есть файл UTF-8 или вы обрабатываете кодирование, самый простой подход к запуску анализа XML с использованием XmlLite показан в документации для CreateXmlReader
:
//Open read-only input stream
if (FAILED(hr = SHCreateStreamOnFile(argv[1], STGM_READ, &pFileStream)))
{
wprintf(L"Error creating file reader, error is %08.8lx", hr);
return -1;
}
if (FAILED(hr = CreateXmlReader(__uuidof(IXmlReader), (void**) &pReader, NULL)))
{
wprintf(L"Error creating xml reader, error is %08.8lx", hr);
return -1;
}
В вашем случае вы хотите пропустить файл, поэтому вам нужно будет создать IStream
в памяти. У вас есть три основных варианта:
- Рассматривайте вашу строку как буфер памяти и используйте
pMemStream = SHCreateMemStream(szData, cbData)
- Поток из Касабланки в
IStream
создан сCreateStreamOnHGlobal(NULL, true, &pMemStream)
а затем использовать его в качестве источника после завершения его поиска - Создать
IStream
Обертка для Касабланкиconcurrency::streams::istream
что скрывает свою асинхронность заIStream
интерфейс
Когда у вас есть поток, вы должны рассказать об этом своему читателю с помощью IXmlReader:: SetInput.
hr = pReader->SetInput(pStream);
Независимо от вышеупомянутых опций, я предлагаю использовать классы RAII, такие как ATL CComPtr<IStream>
а также CComPtr<IXMLReader>
для переменных они показывают как pFileStream
а также pReader
или мой предложенный pMemStream
, Это также когда вам нужно переопределить любые свойства, скажем, если вам нужно обрабатывать более глубокую рекурсию, чем по умолчанию XmlLite. Тогда это все о чтении файла. Простейший цикл для этого описан в методе IXmlReader::Read; Вот некоторые из наиболее важных частей, но обратите внимание, что я упустил обнаружение ошибок для удобства чтения:
void Summarize(IXmlReader *pReader, LPCWSTR wszType)
{
LPCWSTR wszNamespaceURI, wszPrefix, wszLocalName, wszValue;
UINT cchNamespaceURI, cchPrefix, cchLocalName, cchValue;
pReader->GetNamespaceURI(&wszNamespaceURI, &cchNamespaceURI);
pReader->GetPrefix(&wszPrefix, &cchPrefix);
pReader->GetLocalName(&wszLocalName, &cchLocalName);
pReader->GetValue(&wszValue, &cchValue);
std::wcout << wszType << L": ";
if (cchNamespaceURI) std::wcout << L"{" << wszNamespaceURI << L"} ";
if (cchPrefix) std::wcout << wszPrefix << L":";
std::wcout << wszLocalName << "='" << wszValue << "'\n";
}
void Parse(IXmlReader *pReader)
{
// Read through each node until the end
while (!pReader->IsEOF())
{
hr = pReader->Read(&nodeType);
if (hr != S_OK)
break;
switch (nodeType)
{
// : : :
case XmlNodeType_Element:
Summarize(pReader, L"BeginElement");
while (S_OK == pReader->MoveToNextAttribute())
Summarize(pReader, L"Attribute");
pReader->MoveToElement();
if (pReader->IsEmptyElement())
std::wcout << L"EndElement\n";
break;
case XmlNodeType_EndElement:
std::wcout << L"EndElement\n";
break;
// : : :
}
}
}
Некоторые другие части этого примера кода включают проверку E_PENDING
что может иметь значение, если весь файл еще не доступен. Скорее всего, было бы "лучше" иметь Касабланку http_resposne::body
кормить обычай IStream
реализация того, что XmlLite может начать обработку параллельно с его загрузкой; эта ветка обсуждения охватывает эту идею, но, похоже, не имеет канонического решения. По моему опыту, XmlLite настолько быстр, что вызываемая им задержка не имеет значения, поэтому обработки его из полного файла может быть достаточно, особенно если вам требуется полный файл, прежде чем вы сможете завершить свою обработку.
Если вам нужно лучше интегрировать это в асинхронную систему, будет больше обручей. Очевидно, что while
Цикл выше не сам асинхронный. Я предполагаю, что правильный способ сделать его асинхронным будет сильно зависеть от содержимого вашего файла и от обработки, которую вы должны выполнять при чтении, а также от того, привязываете ли вы его к пользовательскому IStream
это может не иметь всех своих данных. Поскольку у меня нет никакого опыта работы с асинхронностью Касабаланки, я не могу комментировать это с пользой.
Это адрес того, что вы ищете, или это была часть, которую вы уже знали, и вы искали IStream
фантик Касабаланка http_response::body
или советы по асинхронной обработке XmlLite?