crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )


  Ответ в QXmlStreamReader и большое количество уровней
Введите ваше имя
Подтвердите код

Введите в поле код из 6 символов, отображенных в виде изображения. Если вы не можете прочитать код с изображения, нажмите на изображение для генерации нового кода.
 

Опции сообщения
 Включить смайлы?
Иконки сообщения
(Опционально)
                                
                                
  [ Без иконки ]
 


Последние 10 сообщений [ в обратном порядке ]
SABROG Дата 29.8.2009, 14:40
  Прикрепленный файл  xmlstreamreaderhelper.zip ( 2.43 килобайт ) Кол-во скачиваний: 322


В общем я немного заморочился на эту тему и написал класс, который назвал по-страшному - xmlstreamreaderhelper :)

Несмотря на то, что понять как он работает не просто я постараюсь все же объяснить суть. Все построено на полиморфизме, чтобы избежать дублирование кода. Есть 2 класса:
  • AbstractTagImplementator
  • AbstractTagProcessor


Класс AbstractTagProcessor наследует класс AbstractTagImplementator и определяет в себе ряд методов для работы с тэгами xml'я. В то время как класс AbstractTagImplementator определяет общий функционал парсера.

Если разбирать мой xml из прошлых постов, то его парсинг теперь выглядит так:

//определяем в заголовке набор классов, каждый из которых представление необходимых тегов
//.h
class TourML : public AbstractTagProcessor
{
public:
    TourML(const QString &name = QString(), AbstractTagImplementator *parent = 0);
    bool beforeEvent();
    void afterEvent();
private:
    QTime t;
};

class Country : public AbstractTagProcessor
{
public:
    Country(const QString &name = QString(), AbstractTagImplementator *parent = 0);
    bool beforeEvent();
};

class Packet : public AbstractTagProcessor
{
public:
    Packet(const QString &name = QString(), AbstractTagImplementator *parent = 0);
    bool beforeEvent();
};


В этих классах переопределяются так называемые эвенты базовых классов. Всего 3 эвента: beforeEvent(), event(), afterEvent(). В зависимости от сложности парсера переопределяются нужные. В простом случае нужен только beforeEvent(), а вот если нужно будет перебрать все существующие теги или обработать неизвестные, или просто хочется реализовать свой функционал иначе, то надо переопределять метод event().

//реализация классов тегов
//.cpp
TourML::TourML(const QString &name, AbstractTagImplementator *parent) :
        AbstractTagProcessor(name, parent)
{
}

bool TourML::beforeEvent()
{
    t.start();
    moveToTag("TourML");
    if (xml()->attributes().value("version") != "1.0") {
        xml()->raiseError(QObject::tr("The file is not an TourML version 1.0 file."));
        return true;
    }
    return false;
}

void TourML::afterEvent()
{
    qDebug() << t.elapsed() << "ms.";
}

Country::Country(const QString &name, AbstractTagImplementator *parent) :
        AbstractTagProcessor(name, parent)
{
}

bool Country::beforeEvent()
{
    qDebug() << xml()->tokenString() << xml()->name() << name() << "Country: " << xml()->attributes().value(QLatin1String("nameLat")).toString();
    return true; // interrupt parse
}

Packet::Packet(const QString &name, AbstractTagImplementator *parent) :
        AbstractTagProcessor(name, parent)
{
}

bool Packet::beforeEvent()
{
    QString packet;
    moveToTag(QLatin1String("tour"));
    packet = xml()->attributes().value(QLatin1String("name")).toString();
    moveToTag(QLatin1String("spo"));
    packet += " (" + xml()->attributes().value(QLatin1String("issue")).toString() + ')';
    skipSubTree();
    moveToTag(QLatin1String("priceQuantity"));
    packet += " [" + xml()->readElementText() + ']';
    qDebug() << packet;
    return true; //parse next tag
}


//Подготовка и вызов парсера
//.cpp
    QString fileName = "file.xml";
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("Xml Parser"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(fileName)
                             .arg(file.errorString()));
        return;
    }

    reader = QSharedPointer<QXmlStreamReader>(new QXmlStreamReader(&file));
    tourMLparser = QSharedPointer<TourML>(new TourML("TourML"));
    TourML::setXmlReader(reader.data());
    AbstractTagImplementator references("references", tourMLparser.data());
    Country country("country", &references);
    AbstractTagImplementator sources("sources", tourMLparser.data());
    Packet packet("packet", &sources);

    if (!tourMLparser->parse() && tourMLparser->xml()->hasError() && tourMLparser->xml()->error() != QXmlStreamReader::PrematureEndOfDocumentError) {
        qDebug() << "Error when parsing data from socket.";
    }


Т.е. открываем файл, создаем объект QXmlStreamReader, которому и передаем device (file). Затем создается родительский объект TourML, и по аналогии с QObject'ами тэги-классы выстраиваются в дерево. Потом на корневом теге вызывается метод parse(), который уже проходит по всему дереву детей. Если в процессе парсинга любого тэга xml прервется, при передаче по сокету например, то запускается локальный QEventLoop и ожидает новые данные с устройства. Если они приходят, то парсинг продолжается дальше. Я пробовал генерировать бесконечный поток xml'я по сокету, парсер работает не прерываясь.

Имена, которые передаются в конструкторы объектов - реальные названия тегов в xml'е чувствительные к регистру. Еще вот этот код прокомментирую:

AbstractTagImplementator references("references", tourMLparser.data());


В этом случае тэг "references" является всего-лишь "чекпоинтом", нам тег не нужен сам по себе, но мы должны указать короткую дорогу парсеру, чтобы он мог найти тег "country", поэтому нам нет необходимости наследоваться от класса AbstractTagImplementator и мы используем его базовый функционал, которого достаточно. С другой стороны мы могли бы отказаться от этого чекпоинта и парсер бы все равно нашел нужный тег, однако в разных ситуациях это может замедлить скорость парсинга, т.к. заставит парсер сравнивать имена ключей, которые мы могли бы отмести заранее. А в случае, когда у разных подветок имеются ключи с одинаковым названием это может привести к путанице и невозможности узнать какой из подветок принадлежит тег, чтобы соотнести его.

Как по мне, так это выглядит более правильно, чем мой первоначальный вариант:

Раскрывающийся текст
Q_ASSERT(isStartElement() && name() == QLatin1String("TourML"));

    while (!atEnd()) {
        readNext();
        if (isStartElement()) {
            if (name() == QLatin1String("references")) {
                while (!(isEndElement() && name() == QLatin1String("references"))) {
                    readNext();
                    if (isStartElement() && name() == QLatin1String("countries")) {
                        while (!(isEndElement() && name() == QLatin1String("countries"))) {
                            readNext();
                            if (isStartElement() && name() == QLatin1String("country"))
                                qDebug() << attributes().value(QLatin1String("nameLat"));
                        }
                    }
                }
            } else if (name() == QLatin1String("sources")) {
                while (!(isEndElement() && name() == QLatin1String("sources"))) {
                    readNext();

                    if (isStartElement() && name() == QLatin1String("source")) {
                        while (!(isEndElement() && name() == QLatin1String("source"))) {
                            readNext();
                            if (isStartElement() && name() == QLatin1String("packets")) {
                                while (!(isEndElement() && name() == QLatin1String("packets"))) {
                                    readNext();
                                    if (isStartElement() && name() == QLatin1String("packet")) {
                                        while (!(isEndElement() && name() == QLatin1String("packet"))) {
                                            readNext();
                                            if (isStartElement() && name() == QLatin1String("packetHeader")) {
                                                while (!(isEndElement() && name() == QLatin1String("packetHeader"))) {
                                                    readNext();
                                                    if (isStartElement()) {
                                                        if (name() == QLatin1String("tour")) {
                                                            qDebug() << attributes().value(QLatin1String("name"));
                                                        } else if (name() == QLatin1String("spo")) {
                                                            qDebug() << attributes().value(QLatin1String("issue"));
                                                        } else if (name() == QLatin1String("spoInfo")) {
                                                            while (!(isEndElement() && name() == QLatin1String("spoInfo"))) {
                                                                readNext();
                                                                if (isStartElement() && name() == QLatin1String("priceQuantity"))
                                                                    qDebug() << readElementText();
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }


К тому же замеры скорости не показали каких либо отличительных различий. Классы на C++ я проектирую впервые поэтому был бы рад узнать где я сделал что-то не так и как этом можно улучшить.

Есть мысль написать кодо-генератор для парсера, где можно было бы загрузить дерево xml'я и проставляя галочки выбрать путь для парсера.

Выкладываю для критики и дополнений исходники.

Прикрепленный файл  xmlstreamreaderhelper.zip ( 2.43 килобайт ) Кол-во скачиваний: 322
Litkevich Yuriy Дата 24.7.2009, 13:39
 
Цитата(SABROG @ 24.7.2009, 17:27) *
я немного успокоился, раз уж сами Qt'шники занимаются копи-пастингом
:)
SABROG Дата 24.7.2009, 13:27
  Сегодня заметил статью в блоге на QtSoftware и о том, что в демке используется QXmlStreamReader. Глянув на код я немного успокоился, раз уж сами Qt'шники занимаются копи-пастингом (насчитал 3 вложенных цикла while)...

Сиськи Здесь
#define GET_DATA_ATTR xml.attributes().value("data").toString()

    void digest(const QString &data) {

        QColor textColor = palette().color(QPalette::WindowText);
        QString unitSystem;

        delete m_iconItem;
        m_iconItem = new QGraphicsSvgItem();
        m_scene.addItem(m_iconItem);
        m_iconItem->setParentItem(m_statusItem);
        qDeleteAll(m_dayItems);
        qDeleteAll(m_conditionItems);
        qDeleteAll(m_rangeItems);
        qDeleteAll(m_forecastItems);
        m_dayItems.clear();
        m_conditionItems.clear();
        m_rangeItems.clear();
        m_forecastItems.clear();

        QXmlStreamReader xml(data);
        while (!xml.atEnd()) {
            xml.readNext();
            if (xml.tokenType() == QXmlStreamReader::StartElement) {
                if (xml.name() == "city") {
                    city = GET_DATA_ATTR;
                    setWindowTitle(city);
                }
                if (xml.name() == "unit_system")
                    unitSystem = xml.attributes().value("data").toString();
                // Parse current weather conditions
                if (xml.name() == "current_conditions") {
                    while (!xml.atEnd()) {
                        xml.readNext();
                        if (xml.name() == "current_conditions")
                            break;
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
                            if (xml.name() == "condition") {
                                m_conditionItem->setPlainText(GET_DATA_ATTR);
                            }
                            if (xml.name() == "icon") {
                                QString name = extractIcon(GET_DATA_ATTR);
                                if (!name.isEmpty()) {
                                    delete m_iconItem;
                                    m_iconItem = new QGraphicsSvgItem(name);
                                    m_scene.addItem(m_iconItem);
                                    m_iconItem->setParentItem(m_statusItem);
                                }
                            }
                            if (xml.name() == "temp_c") {
                                QString s = GET_DATA_ATTR + QChar(176);
                                m_temperatureItem->setPlainText(s);
                            }
                        }
                    }
                }
                // Parse and collect the forecast conditions
                if (xml.name() == "forecast_conditions") {
                    QGraphicsTextItem *dayItem  = 0;
                    QGraphicsSvgItem *statusItem = 0;
                    QString lowT, highT;
                    while (!xml.atEnd()) {
                        xml.readNext();
                        if (xml.name() == "forecast_conditions") {
                            if (dayItem && statusItem &&
                                !lowT.isEmpty() && !highT.isEmpty()) {
                                m_dayItems << dayItem;
                                m_conditionItems << statusItem;
                                QString txt = highT + '/' + lowT;
                                QGraphicsTextItem* rangeItem;
                                rangeItem = m_scene.addText(txt);
                                rangeItem->setDefaultTextColor(textColor);
                                m_rangeItems << rangeItem;
                                QGraphicsRectItem *box;
                                box = m_scene.addRect(0, 0, 10, 10);
                                box->setPen(Qt::NoPen);
                                box->setBrush(Qt::NoBrush);
                                m_forecastItems << box;
                                dayItem->setParentItem(box);
                                statusItem->setParentItem(box);
                                rangeItem->setParentItem(box);
                            } else {
                                delete dayItem;
                                delete statusItem;
                            }
                            break;
                        }
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
                            if (xml.name() == "day_of_week") {
                                QString s = GET_DATA_ATTR;
                                dayItem = m_scene.addText(s.left(3));
                                dayItem->setDefaultTextColor(textColor);
                            }
                            if (xml.name() == "icon") {
                                QString name = extractIcon(GET_DATA_ATTR);
                                if (!name.isEmpty()) {
                                    statusItem = new QGraphicsSvgItem(name);
                                    m_scene.addItem(statusItem);
                                }
                            }
                            if (xml.name() == "low")
                                lowT = toCelcius(GET_DATA_ATTR, unitSystem);
                            if (xml.name() == "high")
                                highT = toCelcius(GET_DATA_ATTR, unitSystem);
                        }
                    }
                }

            }
        }

        m_timeLine.stop();
        layoutItems();
        animate(0);
        m_timeLine.start();
    }
SABROG Дата 22.7.2009, 15:33
  Нашел забавную статью про StAX, пример рассматривается на Java и XmlStreamReader.
Забавно в ней то каким образом они решают проблему здоровых циклов while(). Смысл следующий. Создается абстрактный базовый класс типа ComponentParser. Есть 2 ключа "author" и "entry", при вхождении в эти ключи должен вызываться свой парсер. Поэтому на эти ключи создается 2 класса AuthorParser и EntryParser, оба на базе ComponentParser. Создается map (типа QMap) - {"ИмяКлюча":ОбъектНаБазеComponentParser}. Уже начинает напоминать наши callback'и. Внутри каждого такого ComponentParser'а также есть свой map для дочерних узлов. Вызов метода парсинга для ключа выглядит так:

if (delegates.containsKey(element)) {
            ComponentParser parser = (ComponentParser) delegates.get(element);
            parser.parse(staxXmlReader);
          }


delegates - наш map, parser - объект, который отвечает за парсинг конкретного ключа.

На мой взгляд это мало чем отличается от этого:

void Tag1()
{
    while(!atEnd) {
        ...
    }
}

void Tag2()
{
    while(!atEnd) {
        ...
    }
}

void Tag3()
{
    while(!atEnd) {
        ...
    }
}

void Tag4()
{
    while(!atEnd) {
        ...
    }
}


Просто они взяли "детский" пример (глубина дерева 1 уровень) xml'я, да еще умудрились наворотить всего вокруг него, а циклы while() заменили на StaxUtil.moveReaderToElement("name",staxXmlReader);, по сути одно и тоже что и это:

void moveReaderToElement(const QString &name)
{
    while(!atEnd) {
        ...
        if (startElement == name)
        break;
    }
}
.

В моем предыдущем варианте я использовал свой метод readNext(), который пропускал энное количество ключей. В этой статье предпочитают не пропускать ключи, а искать нужный. Минус в том, что это дополнительные операции сравнения строк имен на каждом ключе. Плюс в том, что код выглядит не уродско. Снова надо делать выбор, либо красота кода, либо скорость парсинга.
Таким образом выходит, что для удобной работы в классе QXmlStreamReader не хватает методов типа: nextTag, nextTag("name"), skipSubTree() и возможности работать асинхронно в цикле событий как QNetworkAccessManager, или потокового варианта QXmlStreamReaderThreaded. Дело в том, что если устройство установлено через setDevice, то парсер не увидит новых приходящих данных, скажем по сокету пока у парсера не будет возможности дать отработать циклу событий. QEventLoop хоть и работает, но это имхо костыль. Интерфейс программы размораживается при таком раскладе, значит нужен модальный диалог. Также встает вопрос о прерывании парсинга. Это все таки цикл, значит надо каким-то образом дать знать парсеру, что пора выходить.
---
Кстати в статье упоминается метод parseElement, на самом деле это опечатка, в исходниках метод parse(). Еще нашел в исходниках реализацию метода moveReaderToElement:

  public static void moveReaderToElement(String target, XMLStreamReader reader) throws XMLStreamException {

    // If current element is equal to target

    String readElement = null;
    for (int event = reader.next(); event != XMLStreamConstants.END_DOCUMENT; event = reader.next()) {

      if ((event == XMLStreamConstants.START_ELEMENT) && (reader.getLocalName().equals(target))) {
        return;
      }
    }
  }


Он хорош только для ключей в одном экземпляре. Если в xml файле будут списки типа:

<name>Витя</name>
<name>Петя</name>
<name>Вася</name>
<name>Коля</name>


То нужен цикл while() и проверка на выход из поддерева иначе парсер поползет дальше по всему xml'ю до самого конца. К сожалению, как я уже говорил, пример в статье слабый, хотя и пишется, что ориентирован он на серьезные xml'и. Продумали не все.
SABROG Дата 15.7.2009, 21:35
  Такой вопрос насчет QXmlStreamReader::PrematureEndOfDocumentError. Предположим я выкачиваю xml из инета и тут же паршу. Где-то в середине на каком-нибудь "packet" данные заканчиваются (медленная скорость выкачивания). Мне надо добавить новые данные через addData() в буффер, когда эти данные скачаются. Так вот я не понимаю, при возникновении ошибки у парсера 2 выхода - добавить данные или завершится. Если данных пока нет и надо их подождать, то ничего не остается как завершить парсинг и начать всё заново, когда поступят все данные. Напрашивается вариант использовать waitForReadyRead, но у QNetworkReply вроде как этот метод не реализован. Как же заморозить текущее состояние QXmlStreamReader, создавать QEventLoop чтоль и ждать?
---
Подобный вопрос задавался в рассылке 2008 года, но Thiago оставил этот вопрос без ответа.
---
Проштудировал 2 страницы тем на QtCentre где упоминалось слово QXmlStreamReader, полезной информации ноль. Примеры все шуточные, докачка нигде не реализована. Пошел штудировать QtForum.
---
На QtForum ситуация еще хуже, тем 5-6 и все одно и то же мусолят.
На prog.org'е вообще тишина, 2 темы и все не о том.
На vingrad'e только один человек тему завел.
На sources вообще одна тема и то там попался include, а не тема про парсер
На этом форуме поиск тоже выдал только меня.
В общем с этим классом дорожка не проторенная ни в рунете ни в мире. Похоже есть только один выход изучать примеры на .Net и Java.
SABROG Дата 15.7.2009, 13:16
  Как я и писал выше затык будет в заполнении таблицы. Мы всё-таки дерево парсим, где может быть не один ребенок у одной ветки. Сложно представить как управлять таким автоматом. Я переделал так, мне кажется это золотая середина:

Раскрывающийся текст
bool XmlSpoListParser::read(QIODevice *device)
{
    setDevice(device);

    while (!atEnd()) {
        readNext();

        if (isStartElement()) {
            if (name() == QLatin1String("TourML") && attributes().value(QLatin1String("version")) == QLatin1String("1.0")) {
                readSpoList();
                break;
            } else {
                raiseError(QObject::tr("The file is not an TourML version 1.0 file."));
                break;
            }
        }
    }

    return !error();
}

void XmlSpoListParser::readSpoList()
{
    Q_ASSERT(isStartElement() && name() == QLatin1String("TourML"));

    while (!atEnd()) {
        readNext();

        if (isStartElement()) {
            if (name() ==  QLatin1String("references")) {
                readCountries();
            } else if (name() == QLatin1String("sources")) {
                readTours();
            } else {
                skipSubTree();
            }
        }
}

void XmlSpoListParser::readCountries()
{
    Q_ASSERT(isStartElement() && name() == QLatin1String("references"));

    readNext(); //skip "countries" tag
    readNext(); //skip "Characters" token

    while (!(isEndElement() && name() == QLatin1String("references"))) {
        readNext();
        if (isStartElement()) {
            if (name() == QLatin1String("country")) {
                qDebug() << attributes().value(QLatin1String("nameLat"));
            } else {
                skipSubTree();
            }
        }
    }
}

void XmlSpoListParser::readTours()
{
    Q_ASSERT(isStartElement() && name() == QLatin1String("sources"));

//something like nextTag() in Java, reduce while cycles

    readNext(); //skip "source" tag
    readNext(); //skip "Characters" token
    readNext(); //skip "quotaServices" tag
    readNext(); //skip "Characters" token
    readNext(); //skip "endElement" token
    readNext(); //skip "packets" tag
    readNext(); //skip "Characters" token

    while (!(isEndElement() && name() == QLatin1String("sources"))) {
        readNext();
        if (isStartElement()) {
            if (name() == QLatin1String("packet")) {
                readPacket();
            } else {
                skipSubTree();
            }
        }
    }
}

void XmlSpoListParser::readPacket()
{
    Q_ASSERT(isStartElement() && name() == QLatin1String("packet"));
    readNext(); //skip "packetHeader" tag
    readNext(); //skip "Characters" token

    while (!(isEndElement() && name() == QLatin1String("packet"))) {
        readNext();
        if (isStartElement()) {
            if (name() == QLatin1String("tour"))
                qDebug() << attributes().value(QLatin1String("name"));
            else if (name() == QLatin1String("spo"))
                qDebug() << attributes().value(QLatin1String("issue"));
            else if (name() == QLatin1String("spoInfo")) {
                readNext(); //move to "priceQuantity" tag
                readNext(); //skip "Characters" token
                qDebug() << readElementText();
            } else
                skipSubTree();
        }
    }
}

void XmlSpoListParser::skipSubTree()
{
    Q_ASSERT(isStartElement());

    while (!atEnd()) {
        readNext();
        if (isEndElement())
            break;
        if (isStartElement())
            skipSubTree();
    }
}


Кстати как я не изгалялся над алгоритмом время парсинга почему-то всегда константно: 297ms. Если использую qDebug() для вывода в консоль, то время парсинга увеличивается до 1048ms. Правда у этого варианта тоже есть существенный недостаток, чтобы использовать стриминг надо проверять на ошибку после каждого readNext() иначе есть шанс прочитать несуществующие аттрибуты или тектовые элементы.
Tonal Дата 15.7.2009, 12:38
  То, что тут пытаетесь изобразить, называется конечный автомат. :)
Т. е. нужно написать таблицу состаяний и таблицу переходов.
Таблица переходов - это двумерный массив индексирующийся состоянием и сигналом.
В твоём случае сигнал - это имя тега.
Таблица состояний может просто состаять из указателей на функции обработки.

Тогда код разбора будет примерно такой:
typedef QVector<QHash<int> > transit_table_t;
typedef void(func_t*)(args);
QVector<func_t> state_table_t

transit_table_t transit_table;
state_table_t state_table;

size_t curr_state = 0;
while (!atEnd()) {
  readNext();
  if (!isStartElement())
    continue;
  curr_state = transit_table[curr_state][name()];
  if (func_t func = state_table[curr_state])
    func(args);
}

Заполнение таблиц и точные типы по вкусу. :)
ViGOur Дата 15.7.2009, 12:24
  Так в том то и дело, что в твоем случае как я понял идут уникальные имена подветок и соответственно не нужно отслеживать, а если нужно наследовать, то достаточно ввести еще один параметр в массив:
struct sCallName
{
   QString m_szName;        // Имя XML узла
   void (*m_pFunc)(void*); // Call back функция которую нужно вызвать для этого имени.

   sCallName *m_pParent;  // Родитель
};
и все так же с вектором, а так как родитель в вектор попадет раньше дочки, то и указатель на родителя будет и при изменении расположения элементов и их родителей никаких проблем с изменением и никакой путаницы.

Вплоть до того, что ты сам программно сможешь менять значения, положения элементов и их родителей, а потом сохранять. Только в структуру нужно будет еще несколько аргументов ввести. :)
SABROG Дата 15.7.2009, 11:14
  Да оно всё это понятно, я тоже об этом говорю. Сам подход странный с точки зрения стиля программирования. Это получается некий гибрид Pull и Push парсера. Просто в таком случае тогда проще взять обычный SAX2 (push) парсер - QXmlReader. Но он не поддерживает потоковые данные и не умеет писать xml'и. Да и заполнять надо как-то этот вектор хитро. Например на одну ветку может приходится несколько нужных подветок, тогда придется дублировать имена основной ветки по количеству необходимых дочерних узлов. Или отказываться от вектора и сделать через дерево указателей. Опять же, такое дерево должно отражать некий путь парсинга. Насколько будет удобно изменять этот путь, подстраиваясь под новые форматы xml'ей? А мне надо в итоге написать 4 xml парсера. В принципе как я говорил выше, можно сделать некий аналог XPath'а, распарсить строчки с нужными путями в дерево. Правда в итоге получится новый оверхед. Тут выбор стоит, либо красивый и компактный код, но в жертву приносится скорость парсинга. Либо увеличение размера приложения из-за копи-пастинга и отвратительный код с точки зрения стиля программирования, но шустрый парсер.
ViGOur Дата 15.7.2009, 10:32
  Ты немного не понял, рекурсия c примерно таким условием:

struct sCallName
{
   QString m_szName;   // Имя XML узла
   void (*m_pFunc)(void*); // Call back функция которую нужно вызвать для этого имени.
};

// ...

QVector<sCallName*> m_vecNames;

// Где-то  m_vecNames заполняется
// ...

while( !isEndElement() && name() == m_vecNames[i].m_szName)
{
   readNext();
   m_vecNames[i].(*m_pFunc)( pVoid); // pVoid - это данные которые тебе нужны для обработки или еще чего там...
}
общая идею думаю должна быть понятна. :)
если не понятно, постараюсь описать подробней.
Просмотр темы полностью (откроется в новом окне)
RSS Текстовая версия Сейчас: 28.3.2024, 18:16