Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Обработка XML _ Xml - обращение к дочернему элементу

Автор: FladeX 27.3.2009, 12:18

Структура xml:

<help>
    <page id="page01">
        <title>title of page01</title>
        <message>text</message>
    </page>
    ...
    <page id="page99">
        <title>title of page99</title>
        <message>text</message>
    </page>
</help>

Нужно по id найти в нем <page>, а затем получить оттуда содержимое <message>. Пытался делать например так:
    QDomElement docElem;
    docElem = doc.elementById("page01");
        QDomNodeList lstNodes(docElem.childNodes()));
        for (int n=0; n<lstNodes.count(), n++)
        {
            childElem = lstNodes.at(n)::toElement();
            if (childElem.tagName == "message")
            {
                message = childElem.text();
            }
        }

Но не получается... Как же правильно тогда?

Автор: igor_bogomolov 27.3.2009, 14:22

Начну с того, что в приведенном Вами коде очень много ошибок, что очень неприятно при отладке. Надо быть поокуратнее.

Цитата
QDomElement QDomDocument::elementById ( const QString & elementId )

Returns the element whose ID is equal to elementId. If no element with the ID was found, this function returns a null element.

Since the QDomClasses do not know which attributes are element IDs, this function returns always a null element. This may change in a future version.

Т.е. насколько я понимаю, этот метод всегда вернет нулевой елемент.
Я сделал так:
    QDomElement docElem;
    QDomNodeList lstNodes(domDocument.elementsByTagName("page"));
        for (int n=0; n<lstNodes.count(); n++)
        {
            QDomElement childElem = lstNodes.at(n).toElement();
            if (childElem.attribute("id") == "page01")
            {
                QDomElement child = childElem.lastChild().toElement();
                qDebug() << child.text();
            }
        }

Данный код у меня работает. Может не самое оптимальное решение, кто знает как сделать лучше раскажите :rolleyes:

Автор: FladeX 27.3.2009, 14:57

Выдает ошибку

crosses initialization of ‘QDomNodeList lstNodes’

на строке
            QDomElement childElem = lstNodes.at(n).toElement();

что не так?

Автор: igor_bogomolov 27.3.2009, 15:16

???
Я так понимаю, что childElem у Вас где-то уже объявлен.
Сделайте просто

childElem = lstNodes.at(n).toElement();

И domDocument, в Вашем случае объявлен как doc
Отпишитесь о результате. Если что выложу компилябильный проект.

Автор: FladeX 27.3.2009, 15:23

childElem ранее объявлен не был.
Замена строки не помогла, ошибка та же самая...

Автор: igor_bogomolov 27.3.2009, 15:47

копируем куда-нибудь проект QTDIR/examples/xml/dombookmarks
В файле xbeltree.cpp ищем функцию bool XbelTree::read(QIODevice *device)
Что бы не мучаться, заменяем весь ее код, на следущий:

Раскрывающийся текст
bool XbelTree::read(QIODevice *device)
{
    QString errorStr;
    int errorLine;
    int errorColumn;

    if (!domDocument.setContent(device, true, &errorStr, &errorLine,
                                &errorColumn)) {
        QMessageBox::information(window(), tr("DOM Bookmarks"),
                                 tr("Parse error at line %1, column %2:\n%3")
                                 .arg(errorLine)
                                 .arg(errorColumn)
                                 .arg(errorStr));
        return false;
    }

    QDomElement docElem;
    QDomNodeList lstNodes(domDocument.elementsByTagName("page"));
        for (int n=0; n<lstNodes.count(); n++)
        {
            QDomElement childElem = lstNodes.at(n).toElement();
            if (childElem.attribute("id") == "page01")
            {
                QDomElement child = childElem.lastChild().toElement();
                qDebug() << child.text();
            }
        }

    return true;
}



Рядом с exe-шником кладем ваш файл
Раскрывающийся текст
<?xml version="1.0" encoding="UTF-8"?>
<help>
    <page id = "page01">
        <title>title of page01</title>
        <message>text1</message>
    </page>
    <page id = "page99">
        <title>title of page99</title>
        <message>text2</message>
    </page>
</help>


Компилируем, смотрим что выводит qDebug();
Не забываем отписаться о результатах.

P.S. У меня все выше описанное прекрасно работает.

Автор: Kagami 27.3.2009, 18:15

Есть решение немного по-изящнее:

Раскрывающийся текст
    QDomElement root = domDocument.documentElement();
    QDomElement child = root.firstChildElement("page");
    while (!child.isNull()) {
        if (child.attribute("id") == "page01") {
            QDomElement message = child.firstChildElement("message");
            qDebug() << child.text();
        }
        child = child.nextSiblingElement("page");
    }

Перебираем только нужные элементы и не чувствительны к положению <message> - в предыдущем примере он обязан находиться в конце.

Автор: igor_bogomolov 27.3.2009, 23:27

Цитата(Kagami @ 27.3.2009, 18:15) *
Есть решение немного по-изящнее:

Не буду врать, по специфике своей работы никак с xml сталкиваться не приходилось. Просто было интересно разобраться, вот и помог. Ваш же пример выдаёт не то что мы ожидаем. В топикстарте написано, что требуется вернуть данные тега "message", у Вас же получается следущее
Цитата
"title of page01text1"

а надо
Цитата
"text1"



Цитата(Kagami @ 27.3.2009, 18:15) *
в предыдущем примере он обязан находиться в конце.

Да, я исходил из структуры файла :rolleyes:

Автор: Kagami 27.3.2009, 23:33

Цитата(igor_bogomolov @ 27.3.2009, 23:27) *
Ваш же пример выдаёт не то что мы ожидаем.

Упс, небольшая очепятка всралась :)
Правильно так:
qDebug() << message.text();

Автор: Litkevich Yuriy 28.3.2009, 3:34

Цитата(Kagami @ 28.3.2009, 2:33) *
.text()
по моему опыту, лучше избегать применения этой функции, т.к. она рекурсивная, т.е. схватит все вложенные тэги, часто это неподходящий вариант. Лучше использовать data() или value().

П.С. еслиб не простуда, привел бы пример, но нагретая голова плохо соображает.

Автор: Kagami 28.3.2009, 9:16

Цитата(Litkevich Yuriy @ 28.3.2009, 3:34) *
по моему опыту, лучше избегать применения этой функции, т.к. она рекурсивная, т.е. схватит все вложенные тэги, часто это неподходящий вариант. Лучше использовать data() или value().

Хм.. Что-то я сходу не нашел таких функций у QDomElement и у QDomDocument ни у их родителя QDomNode..

P.S. Выздоравливай скорее

Автор: FladeX 7.4.2009, 11:15

Kagami, спасибо большое! Мне как раз надо чтобы от положения элемента в xml не зависел его выбор.
Но ошибка так и остается:

expertsystem.cpp:174: error: jump to case label                                                                                                                      
expertsystem.cpp:163: error:   crosses initialization of ‘QDomElement child’                                                                                        
expertsystem.cpp:162: error:   crosses initialization of ‘QDomElement root’                                                                                          
expertsystem.cpp:161: error:   crosses initialization of ‘QDomDocument domDocument’

Что за ошибка вообще такая?

Автор: igor_bogomolov 7.4.2009, 11:28

Видимо у вас в коде есть что то вроде такого

     switch (i) {
     case 1:
         int j = 1;
         break;
     case 2:
         int j = 2;
         break;
     }

Так делать нельзя. Определение int j нужно вынести
     switch (i) {
     int j;
     case 1:
         j = 1;
         break;
     case 2:
         j = 2;
         break;
     }

Автор: Litkevich Yuriy 7.4.2009, 11:45

Цитата(Kagami @ 28.3.2009, 13:16) *
Хм.. Что-то я сходу не нашел таких функций у QDomElement и у QDomDocument ни у их родителя QDomNode..
вот кусочки из моего кода:
void XmlStalker::SelfPortrait(const QDomNode &n, int rid, int id)
{
...
value= n.nodeValue().simplified();
...
t = n.toText();
data = t.data().simplified();
...
}
насчёт value() неправ, должно быть nodeValue()

Автор: FladeX 7.4.2009, 13:03

Итак, currentId() содержит айдишник текущей страницы. Обработка нажатия на кнопку "Помощь" (только основное):

cpp
    QString pageid = QString::number(currentId());

    QDomDocument domDocument;
    QDomElement root = domDocument.documentElement();
    QDomElement help = root.firstChildElement("help");
    QDomElement child = help.firstChildElement("page");
    while (!child.isNull())
    {
        if (child.attribute("id") == pageid)
        {
            QDomElement mess = child.firstChildElement("message");
            message = mess.text();
            //message = pageid;
        }
        child = child.nextSiblingElement("page");
    }

И вот такой структуры xml файл есть:
xml
<?xml version="1.0" ?>
<!-- Help data -->
<help>
    <page id="0">
        <title>title0</title>
        <message>message0</message>
    </page>
    <page id="1">
        <title>title1</title>
        <message><![CDATA[message1]]></message>
    </page>
</help>

Но почему-то переменная message пуста...

Автор: Litkevich Yuriy 7.4.2009, 13:36

FladeX, У меня есть тестовая программа по которой я изучал QDOM, вот что она расказывает о твоём файле:

Раскрывающийся текст

Сейчас 07.04.2009 17:36:39.609
Addr: 0, 0
Type: DocumentNode
Name: #document
AName:
Value:
AValue:
Data:
Text:
I have a 3 children
-------------------------------------------------
Addr: 0, 1
Type: ProcessingInstructionNode
Name: xml
AName:
Value: version='1.0'
AValue:
Data:
Text:
I havn't a children
-------------------------------------------------
Addr: 0, 2
Type: CommentNode
Name: #comment
AName:
Value: Help data
AValue:
Data:
Text:
I havn't a children
-------------------------------------------------
Addr: 0, 3
Type: ElementNode
Name: help
AName:
Value:
AValue:
Data:
Text: title0message0title1message1
I have a 2 children
-------------------------------------------------
Addr: 3, 4
Type: ElementNode
Name: page
AName:
Value:
AValue:
Data:
Text: title0message0
I have a 2 children
-------------------------------------------------
Addr: 4, 5
Type: ElementNode
Name: title
AName:
Value:
AValue:
Data:
Text: title0
I have a 1 children
-------------------------------------------------
Addr: 5, 6
Type: TextNode
Name: #text
AName:
Value: title0
AValue:
Data: title0
Text:
I havn't a children
-------------------------------------------------
Addr: 4, 7
Type: ElementNode
Name: message
AName:
Value:
AValue:
Data:
Text: message0
I have a 1 children
-------------------------------------------------
Addr: 7, 8
Type: TextNode
Name: #text
AName:
Value: message0
AValue:
Data: message0
Text:
I havn't a children
-------------------------------------------------
Addr: 3, 9
Type: ElementNode
Name: page
AName:
Value:
AValue:
Data:
Text: title1message1
I have a 2 children
-------------------------------------------------
Addr: 9, 10
Type: ElementNode
Name: title
AName:
Value:
AValue:
Data:
Text: title1
I have a 1 children
-------------------------------------------------
Addr: 10, 11
Type: TextNode
Name: #text
AName:
Value: title1
AValue:
Data: title1
Text:
I havn't a children
-------------------------------------------------
Addr: 9, 12
Type: ElementNode
Name: message
AName:
Value:
AValue:
Data:
Text: message1
I have a 1 children
-------------------------------------------------
Addr: 12, 13
Type: CDATASectionNode
Name: #cdata-section
AName:
Value: message1
AValue:
Data: message1
Text:
I havn't a children
-------------------------------------------------
END of tree
Если надо могу дать её исходник. (правда её нужно доработать, чтобы атрибуты печатала)

П.С. к сожалению DOM не интуитивен, и после паузы в работе с ним всё вылетает из головы. поэтому даже немогу подсказать в чём у тебя ошибка.

Здесть "Addr: *, *" условный адрес узла (адрес родителя, адрес текущего )

Автор: Kagami 7.4.2009, 14:12

А какая область видимости у переменной message?

Автор: FladeX 7.4.2009, 14:30

Только внутри функции. Используется для QMessageBox и все.

Автор: Kagami 7.4.2009, 17:19

В приведенном выше кусочке вроде ошибок не видно. Было бы неплохо глянуть на весь код этой процедуры

Автор: FladeX 8.4.2009, 11:59

Вот функция целиком:

cpp
void ExpertSystem::showHelp()
{
    static QString lastHelpMessage;

    QString message;
    QDomDocument doc;
    QFile file("/home/opensuse/qt/expertsystem/help.xml");
    if (!file.exists())
    {
        QMessageBox::warning(this, QObject::trUtf8("Предупреждение"), QObject::trUtf8("Ошибка 001. Файл help.xml не существует"));
    }
    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::warning(this, QObject::trUtf8("Предупреждение"), QObject::trUtf8("Ошибка 002. Невозможно открыть файл help.xml"));
        return;
    }
    if (!doc.setContent(&file))
    {
        QMessageBox::warning(this, QObject::trUtf8("Предупреждение"), QObject::trUtf8("Ошибка 003. Невозможно прочитать файл help.xml"));
        file.close();
        return;
    }
    doc.setContent(&file);

    QString pageid = QString::number(currentId());

    QDomDocument domDocument;
    QDomElement root = domDocument.documentElement();
    QDomElement help = root.firstChildElement("help");
    QDomElement child = help.firstChildElement("page");
    while (!child.isNull())
    {
        if (child.attribute("id") == pageid)
        {
            QDomElement mess = child.firstChildElement("message");
            message = mess.text();
            //message = pageid;
        }
        child = child.nextSiblingElement("page");
    }
    file.close();

    //if (lastHelpMessage == message)
    //    message = tr("");

    QMessageBox::information(this, QObject::trUtf8("Подсказка"), message);

    lastHelpMessage = message;
}

Автор: Kagami 8.4.2009, 21:59

Посидел десять минут, но раскусил этот орешек:

Раскрывающийся текст
void ExpertSystem::showHelp()
{
static QString lastHelpMessage;

QString message;
QDomDocument doc;
QFile file("/home/opensuse/qt/expertsystem/help.xml");
if (!file.exists())
{
QMessageBox::warning(this, QObject::trUtf8("Предупреждение"), QObject::trUtf8("Ошибка 001. Файл help.xml не существует"));
}
if (!file.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this, QObject::trUtf8("Предупреждение"), QObject::trUtf8("Ошибка 002. Невозможно открыть файл help.xml"));
return;
}
if (!doc.setContent(&file))
{
QMessageBox::warning(this, QObject::trUtf8("Предупреждение"), QObject::trUtf8("Ошибка 003. Невозможно прочитать файл help.xml"));
file.close();
return;
}
//Второй раз устанавливать содержимое не надо
doc.setContent(&file);


// В результате получим pageid равное числу...
QString pageid = QString::number(currentId());
QString pageid = "page" + QString("%1").arg(1, 2, 10, QChar('0'));

//Вот нафига заводить еще один документ? Если мы уже все загрузили в doc
QDomDocument domDocument;
QDomElement root = domDocument.documentElement();

QDomElement root = doc.documentElement();
//help у нас корневой элемент и уже содержится в root. Второй раз его искать не надо
QDomElement help = root.firstChildElement("help");
//С учетом вышесказанного эта строка не правильная
QDomElement child = help.firstChildElement("page");
QDomElement child = root.firstChildElement("page");

while (!child.isNull())
{
if (child.attribute("id") == pageid)
{
QDomElement mess = child.firstChildElement("message");
message = mess.text();
//message = pageid;
}
child = child.nextSiblingElement("page");
}
file.close();

//if (lastHelpMessage == message)
// message = tr("");

QMessageBox::information(this, QObject::trUtf8("Подсказка"), message);

lastHelpMessage = message;
}


P.S. Жалко что нельзя раскрашивать код, только так :(

Автор: FladeX 9.4.2009, 11:46

Спасибо за развернутый ответ! Теперь понятнее стало.
Однако, погоняв эту функцию, заметил, что условие

        if (child.attribute("id") == pageid)

всегда ложно. То есть если внутри этого условия задавать переменную message, то в программе ничего не будет выведено, так как этот участок кода никогда не отработает. А если задавать message вне условной конструкции, то все ок. Почему же так происходит? Может условие не совсем корректное?

Автор: Kagami 9.4.2009, 13:25

Из-за области видимости переменной. Если ее создавать в условии, то в его конце она будет удалена

Автор: FladeX 10.4.2009, 15:00

Не совсем понял, какую переменную мы создаем в условии...
Перенес из цикла все что только можно было вынести - результат тот же (пустое модальное окно). Перенес вызов QMessageBox внутрь цикла - модальное окно вообще перестало вызываться.

Автор: Kagami 10.4.2009, 18:25

А какие значение возвращает currentId() ?

Автор: FladeX 13.4.2009, 14:15

Возвращает int.

Но я уже решил проблему:
Вместо этого:

    QString pageid = "page" + QString("%1").arg(1,2,10,QChar('0'));

прописал старое:
    QString pageid = QString::number(currentId());

и все заработало! Отдельное спасибо Kagami за помощь :)

Автор: Litkevich Yuriy 13.4.2009, 15:14

Цитата(FladeX @ 13.4.2009, 18:15) *
Отдельное спасибо Kagami за помощь
кни ему "Спасибу"

Автор: Kagami 13.4.2009, 18:59

Если бы я заметил что в середине обсуждения у тебя поменялся xml-файл все решилось бы быстрее :)

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)