Я хочу сделать запрос к веб-службе, получить содержимое 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 довольно маленький и содержит один или несколько (если адрес неоднозначный) элементов элемента с атрибутами 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?
Я еще не использовал Касабланку, так что это может быть немного не так. (Я бы хотел поработать с Касабланкой, но сначала мне придется собрать больше времени.) Тем не менее, похоже, что код, который вы показываете, загрузит файл 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 :: Читать Способ; Вот некоторые из наиболее важных частей, но обратите внимание, что я упустил обнаружение ошибок для удобства чтения:
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?