crossplatform.ru

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

2 страниц V  < 1 2  
Ответить в данную темуНачать новую тему
> QXmlStreamReader и большое количество уровней
Tonal
  опции профиля:
сообщение 15.7.2009, 12:38
Сообщение #11


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


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

Тогда код разбора будет примерно такой:
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);
}

Заполнение таблиц и точные типы по вкусу. :)

Сообщение отредактировал Tonal - 15.7.2009, 12:42
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 15.7.2009, 13:16
Сообщение #12


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Как я и писал выше затык будет в заполнении таблицы. Мы всё-таки дерево парсим, где может быть не один ребенок у одной ветки. Сложно представить как управлять таким автоматом. Я переделал так, мне кажется это золотая середина:

Раскрывающийся текст
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() иначе есть шанс прочитать несуществующие аттрибуты или тектовые элементы.

Сообщение отредактировал SABROG - 15.7.2009, 14:54
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 15.7.2009, 21:35
Сообщение #13


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Такой вопрос насчет 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.

Сообщение отредактировал Litkevich Yuriy - 15.7.2009, 22:57
Причина редактирования: поправил ссылку на lists.trolltech.com
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 22.7.2009, 15:33
Сообщение #14


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Нашел забавную статью про 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 - 23.7.2009, 15:16
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 24.7.2009, 13:27
Сообщение #15


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Сегодня заметил статью в блоге на 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 - 24.7.2009, 16:56
Причина редактирования: для длинных исходников используй тэг expand
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 24.7.2009, 13:39
Сообщение #16


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9669
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


Цитата(SABROG @ 24.7.2009, 17:27) *
я немного успокоился, раз уж сами Qt'шники занимаются копи-пастингом
:)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 29.8.2009, 14:40
Сообщение #17


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Прикрепленный файл  xmlstreamreaderhelper.zip ( 2.43 килобайт ) Кол-во скачиваний: 306


В общем я немного заморочился на эту тему и написал класс, который назвал по-страшному - 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 килобайт ) Кол-во скачиваний: 306
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V  < 1 2
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 9.12.2022, 16:55