crossplatform.ru

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

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> QXmlStreamReader и большое количество уровней
SABROG
  опции профиля:
сообщение 13.7.2009, 14:50
Сообщение #1


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

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

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




Репутация:   34  


Решил попробовать класс QXmlStreamReader. Изучив пример streambookmarks я реализовал свой парсер. И вот что мне не понравилось. Можно написать простенький код, который будет сравнивать каждое название ключа на токен isStartElement с нужным нам. Недостаток такого метода - большое количество операций сравнений и падение скорости. Зато очень просто, когда ключи имеют уникальные имена, не нужно контролировать вложенность, родительские отношения. Второй вариант (правильный). На каждый уровень дерева выделяется отдельный метод с while(!atEnd()). Подобное выделение требует создание нового метода для каждого такого уровня. Если глубина дерева в xml'e большая, то можно себе представить какое огромное количество методов получится! Например у меня глубина дерева такая:

tourml/sources/source/packets/packet/packetheader/spo/dates/date


Т.е. 9 методов (на каждый уровень) и в каждом по циклу while(). При этом меня совершенно не интересуют промежуточные уровни, только последние.

Теперь сижу и думаю может сделать какие-нибудь указатели на методы, которые будут вызываться по очереди на одном while. Скажем QQueue/QHash с указателем на метод и строкой сравнения.
---
Только вот на самом деле там даже больше 9и методов получится, т.к. в xml'е еще другие блоки есть, такой например:

tourml/references/tourtypes/tourtype


Сообщение отредактировал SABROG - 13.7.2009, 14:59
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 13.7.2009, 15:56
Сообщение #2


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

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




Репутация:   40  


Код в студию, можно занятся оптимизаторством и универсальностью кода. :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 13.7.2009, 17:45
Сообщение #3


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

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

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




Репутация:   34  


Цитата(ViGOur @ 13.7.2009, 16:56) *
Код в студию, можно занятся оптимизаторством и универсальностью кода. :)

Давайте попробуем, коль не боитесь :)

Вот простой вариант:

    Q_ASSERT(isStartElement() && name() == "TourML");

    while (!atEnd()) {
        readNext();
        if (isStartElement()) {
            if (name() == "country") {
                qDebug() << attributes().value("nameLat");
            } else if (name() == "tour") {
                qDebug() << attributes().value("name");
            } else if (name() == "spo") {
                qDebug() << attributes().value("issue");
            } else if (name() == "priceQuantity") {
                qDebug() << readElementText();
            }
        }
}


Недостаток в том, что нет разбора кому какой ключ принадлежит. Т.е. если в xml'e будет 3 ветки и в каждой подветке будут ключи с одинаковыми именами, то я не смогу разобрать ключи "по кучкам".

А есть такая махина:

    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();
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }


Соответственно тут и так всё понятно. Плюс в том, что тут я точно знаю какой ветке что принадлежит.

По скорости и первый и второй варианты почти одинаковы. XML файл на 5,5Мб парсится ~297ms.

Сообщение отредактировал SABROG - 13.7.2009, 18:21
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 13.7.2009, 19:29
Сообщение #4


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

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




Репутация:   40  


Жесть!

А почему нельзя сделать рекурсию с созданием дерева наследников? Меньше копи/паста одного и того же кода.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 13.7.2009, 20:28
Сообщение #5


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

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

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




Репутация:   34  


Цитата(ViGOur @ 13.7.2009, 20:29) *
А почему нельзя сделать рекурсию с созданием дерева наследников?


Я об этом думал пока ехал домой. Завтра попробую сделать рекурсию, но без дерева наследника. Попробую передавать в рекурсивную функцию нужные мне ключи и уже фильтровать их через if (name() == parName).
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 14.7.2009, 14:25
Сообщение #6


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

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

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




Репутация:   34  


Да уж, что-то не очень получается. Если делать рекурсию без дерева наследников, то получается каждый ключ сравнивается со всем списком ключей снова, это замедляет парсинг. Есть мысль сделать дерево наследников таким образом: "tourml/sources/source/packets/packet/packetheader/spo/dates/date" + QStringList list = str.split("/");

Затем в рекурсии проходить по элементам списка и удалять из него пройденные ключи. Но этот вариант мне кажется каким-то странным, слабеньким вариантом XPath.
---
Так, теперь я узнал, что есть разные варианты парсеров: DOM, SAX, StAX. Причем QXmlStreamReader это StAX парсер, который по сути вырос из Pull парсера. StAX и SAX совершенно разные вещи, первый не использует callback функции в отличие от второго.

Сообщение отредактировал SABROG - 14.7.2009, 21:25
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 14.7.2009, 22:40
Сообщение #7


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

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

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




Репутация:   34  


В общем посмотрел я десяток примеров использования StAX парсера на Java и пришел к выводу, что это, в принципе, нормальный код. В основном, как я и говорил люди выносят циклы while() в отдельные методы и жестко вызывают метод nextTag() (которого кстати нет в Qt, а он пропускает комментарии всё остальное, что не является tag'ом), который устанавливает курсор на следующий элемент, подразумевая тема самым, что если сначала шел Tag = "Name", то за ним обязательно должен идти Tag = "Surname". Т.е. надеются на жесткий порядок следования tag'ов, чтобы уменьшить количество итераций через while(). Тем не менее почти везде такие здоровые портянки. Нигде не обнаружил рекурсии или какой-то специальной оптимизации.

Сообщение отредактировал SABROG - 14.7.2009, 23:06
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 15.7.2009, 10:32
Сообщение #8


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

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




Репутация:   40  


Ты немного не понял, рекурсия 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 - это данные которые тебе нужны для обработки или еще чего там...
}
общая идею думаю должна быть понятна. :)
если не понятно, постараюсь описать подробней.

Сообщение отредактировал ViGOur - 15.7.2009, 10:37
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 15.7.2009, 11:14
Сообщение #9


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

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

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




Репутация:   34  


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


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

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




Репутация:   40  


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

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

Вплоть до того, что ты сам программно сможешь менять значения, положения элементов и их родителей, а потом сохранять. Только в структуру нужно будет еще несколько аргументов ввести. :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

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


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




RSS Текстовая версия Сейчас: 26.12.2024, 19:53