Локальные переменные static
, вероятно, отбрасывают вашу обработку.Помните, что локальная переменная static
сохраняет свое значение между вызовами функций.После выхода streamFile()
и его повторного вызова переменные static
по-прежнему будут иметь свои прежние значения, они не будут возвращены к своим первоначальным значениям.Вместо этого вам придется изменить их, чтобы они стали членами вашего CXMLManager
класса, чтобы streamFile()
мог сбрасывать их при каждом вызове.
Я не предлагаю использовать одну функцию, чтобы попытаться обработать каждыйвозможный узел нужно разобрать.Я бы разбил чтение на отдельные функции, которые имеют свои обязанности на каждом уровне XML-документа, что-то вроде этого:
void CXMLManager::readFeed(xmlTextReaderPtr reader)
{
// read attributes if needed...
if (xmlTextReaderIsEmptyElement(reader))
return;
int depth = xmlTextReaderNodeDepth(reader);
int ret;
while ((ret = xmlTextReaderRead(reader)) == 1)
{
switch (xmlTextReaderNodeType(reader))
{
case XML_READER_TYPE_ELEMENT:
{
if (xmlStrEqual(xmlTextReaderConstLocalName(reader), BAD_CAST "entry"))
{
CFeed entry;
readFeedEntry(reader, entry);
m_feedBuffer.push_back(entry);
}
break;
}
case XML_READER_TYPE_END_ELEMENT:
{
if ((xmlTextReaderNodeDepth(reader) == depth)
/*&& xmlStrEqual(xmlTextReaderConstLocalName(reader), BAD_CAST "feed")*/)
{
return;
}
break;
}
}
}
if (ret == -1)
throw CFeedreaderException("FEEDREADER: Failed to read XML.", ...);
}
void CXMLManager::readFeedEntry(xmlTextReaderPtr reader, CFeed &entry)
{
// read attributes if needed...
if (xmlTextReaderIsEmptyElement(reader))
return;
int depth = xmlTextReaderNodeDepth(reader);
int ret;
while ((ret = xmlTextReaderRead(reader)) == 1)
{
switch (xmlTextReaderNodeType(reader))
{
case XML_READER_TYPE_ELEMENT:
{
const xmlChar *name = xmlTextReaderConstLocalName(reader);
if (xmlStrEqual(name, BAD_CAST "title"))
{
readText(reader, entry.m_title/*, BAD_CAST "title"*/);
std::cout << "Title: " << entry.m_title << std::endl;
}
// else other <entry> children as needed ...
break;
}
case XML_READER_TYPE_END_ELEMENT:
{
if ((xmlTextReaderNodeDepth(reader) == depth)
/*&& xmlStrEqual(xmlTextReaderConstLocalName(reader), BAD_CAST "entry")*/)
{
return;
}
break;
}
}
}
if (ret == -1)
throw CFeedreaderException("FEEDREADER: Failed to read XML.", ...);
}
void CXMLManager::readText(xmlTextReaderPtr reader, std::string &text/*, const xmlChar *tagName */)
{
text.clear();
if (xmlTextReaderIsEmptyElement(reader))
return;
int depth = xmlTextReaderNodeDepth(reader);
int ret;
while ((ret = xmlTextReaderRead(reader)) == 1)
{
switch (xmlTextReaderNodeType(reader))
{
// TODO: handle XML_READER_TYPE_ELEMENT if you need to treat
// embedded XML elements as part of the text, such as for
// formatting instructions (like <b>, <i>, etc)...
case XML_READER_TYPE_TEXT:
{
const xmlChar *value = xmlTextReaderConstValue(reader);
text += reinterpret_cast<const char*>(value);
break;
}
case XML_READER_TYPE_END_ELEMENT:
{
if ((xmlTextReaderNodeDepth(reader) == depth)
/*&& xmlStrEqual(name, tagName)*/)
{
return;
}
break;
}
}
}
if (ret == -1)
throw CFeedreaderException("FEEDREADER: Failed to read XML.", ...);
}
void CXMLManager::streamFile(const char *data, size_t size)
{
/*
* Pass some special parsing options to activate DTD attribute defaulting,
* entities substitution and DTD validation
*/
xmlTextReaderPtr reader = xmlReaderForMemory(data, size, NULL, NULL,
XML_PARSE_DTDATTR | /* default DTD attributes */
XML_PARSE_NOENT); /* substitute entities */
if (!reader)
throw CFeedreaderException("FEEDREADER: Failed to parse XML.", E_WRONG_XML);
std::unique_ptr<xmlTextReader, decltype(xmlFreeTextReader)> reader_deleter(reader, xmlFreeTextReader);
int ret;
while ((ret = xmlTextReaderRead(reader)) == 1)
{
if ((xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
&& xmlStrEqual(xmlTextReaderConstLocalName(reader), BAD_CAST "feed"))
{
readFeed(reader);
}
}
if (ret == -1)
throw CFeedreaderException("FEEDREADER: Failed to read XML.", ...);
}
В качестве альтернативы, я бы предложил полностью избавиться от всех вспомогательных функций и простоделать все внутри самого streamFile()
, используя локальный конечный автомат при циклическом просмотре reader
, например:
void CXMLManager::streamFile(const char *data, size_t size)
{
/*
* Pass some special parsing options to activate DTD attribute defaulting,
* entities substitution and DTD validation
*/
xmlTextReaderPtr reader = xmlReaderForMemory(data, size, NULL, NULL,
XML_PARSE_DTDATTR | /* default DTD attributes */
XML_PARSE_NOENT); /* substitute entities */
if (!reader)
throw CFeedreaderException("FEEDREADER: Failed to parse XML.", E_WRONG_XML);
std::unique_ptr<xmlTextReader, decltype(xmlFreeTextReader)> reader_deleter(reader, xmlFreeTextReader);
std::string name, title, updated, author, link, text;
int feedDepth = -1;
int entryDepth = -1;
int textDepth = -1;
int ret;
while ((ret = xmlTextReaderRead(reader)) == 1)
{
switch (xmlTextReaderNodeType(reader))
{
case XML_READER_TYPE_ELEMENT:
{
if (textDepth != -1)
{
// TODO: handle this case if you need to treat embedded
// XML elements as part of the text, such as for formatting
// instructions (like <b>, <i>, etc)...
break;
}
const xmlChar *name = xmlTextReaderConstLocalName(reader);
if (feedDepth == -1)
{
if (xmlStrEqual(name, BAD_CAST "feed"))
{
// read attributes if needed...
feedDepth == xmlTextReaderNodeDepth(reader);
}
}
else if (entryDepth == -1)
{
if (xmlStrEqual(name, BAD_CAST "entry"))
{
name = title = updated = author = link = text = "";
// read attributes if needed...
if (xmlTextReaderIsEmptyElement(reader))
m_feedBuffer.push_back( CFeed { name, title, updated, author, link } );
else
entryDepth == xmlTextReaderNodeDepth(reader);
}
}
else if (xmlStrEqual(name, BAD_CAST "title"))
{
text.clear();
if (!xmlTextReaderIsEmptyElement(reader))
textDepth = xmlTextReaderNodeDepth(reader);
else
textDepth = -1;
}
// else other <entry> children as needed ...
break;
}
case XML_READER_TYPE_TEXT:
{
if (textDepth != -1)
{
const xmlChar *value = xmlTextReeaderConstValue(reader);
text += reinterpret_cast<const char*>(value);
}
break;
}
case XML_READER_TYPE_END_ELEMENT:
{
const xmlChar *name = xmlTextReaderConstLocalName(reader);
if (textDepth != -1)
{
if ((xmlTextReaderNodeDepth(reader) == textDepth)
/*&& xmlStrEqual(name, BAD_CAST "title")*/)
{
textDepth = -1;
title = text;
text.clear();
std::cout << "Title: " << title << std::endl;
}
// else other <entry> children as needed ...
}
else if (entryDepth != -1)
{
if ((xmlTextReaderNodeDepth(reader) == entryDepth)
/*&& xmlStrEqual(name, BAD_CAST "entry")*/)
{
entryDepth = -1;
m_feedBuffer.push_back( CFeed { name, title, updated, author, link } );
}
}
else if (feedDepth != -1)
{
if ((xmlTextReaderNodeDepth(reader) == feedDepth)
/*&& xmlStrEqual(name, BAD_CAST "feed")*/)
{
feedDepth = -1;
}
}
break;
}
}
}
if (ret == -1)
throw CFeedreaderException("FEEDREADER: Failed to read XML.", ...);
}