crossplatform.ru

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


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

Введите в поле код из 6 символов, отображенных в виде изображения. Если вы не можете прочитать код с изображения, нажмите на изображение для генерации нового кода.
Теги
Выровнять по центру
Ссылка на тему
Ссылка на сообщение
Скрытый текст
Сокращение
Код с подсветкой
Offtopic
 
Удалить форматирование
Спец. элементы
Шрифт
Размер
 
Цвет шрифта
 
Отменить ввод
Вернуть ввод
Полужирный
Курсив
Подчеркнутый
 
 
Смайлики
Вставить изображение
Вставить адрес электронной почты
Цитата
Код
Раскрывающийся текст
 
Увеличить отступ
По левому краю
По центру
По правому краю
Вставить список
Вставить список

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


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


В общем я немного заморочился на эту тему и написал класс, который назвал по-страшному - 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 килобайт ) Кол-во скачиваний: 349
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 Рейтинг@Mail.ru Текстовая версия Сейчас: 11.7.2025, 3:54