Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Xml - обращение к дочернему элементу
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Обработка XML
FladeX
Структура 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
Начну с того, что в приведенном Вами коде очень много ошибок, что очень неприятно при отладке. Надо быть поокуратнее.
Цитата
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
Выдает ошибку
crosses initialization of ‘QDomNodeList lstNodes’

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

что не так?
igor_bogomolov
???
Я так понимаю, что childElem у Вас где-то уже объявлен.
Сделайте просто
childElem = lstNodes.at(n).toElement();

И domDocument, в Вашем случае объявлен как doc
Отпишитесь о результате. Если что выложу компилябильный проект.
FladeX
childElem ранее объявлен не был.
Замена строки не помогла, ошибка та же самая...
igor_bogomolov
копируем куда-нибудь проект 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
Есть решение немного по-изящнее:
Раскрывающийся текст
    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
Цитата(Kagami @ 27.3.2009, 18:15) *
Есть решение немного по-изящнее:

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

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



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

Да, я исходил из структуры файла :rolleyes:
Kagami
Цитата(igor_bogomolov @ 27.3.2009, 23:27) *
Ваш же пример выдаёт не то что мы ожидаем.

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

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

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

P.S. Выздоравливай скорее
FladeX
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
Видимо у вас в коде есть что то вроде такого
     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
Цитата(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
Итак, 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
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
А какая область видимости у переменной message?
FladeX
Только внутри функции. Используется для QMessageBox и все.
Kagami
В приведенном выше кусочке вроде ошибок не видно. Было бы неплохо глянуть на весь код этой процедуры
FladeX
Вот функция целиком:
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
Посидел десять минут, но раскусил этот орешек:
Раскрывающийся текст
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
Спасибо за развернутый ответ! Теперь понятнее стало.
Однако, погоняв эту функцию, заметил, что условие
        if (child.attribute("id") == pageid)

всегда ложно. То есть если внутри этого условия задавать переменную message, то в программе ничего не будет выведено, так как этот участок кода никогда не отработает. А если задавать message вне условной конструкции, то все ок. Почему же так происходит? Может условие не совсем корректное?
Kagami
Из-за области видимости переменной. Если ее создавать в условии, то в его конце она будет удалена
FladeX
Не совсем понял, какую переменную мы создаем в условии...
Перенес из цикла все что только можно было вынести - результат тот же (пустое модальное окно). Перенес вызов QMessageBox внутрь цикла - модальное окно вообще перестало вызываться.
Kagami
А какие значение возвращает currentId() ?
FladeX
Возвращает int.

Но я уже решил проблему:
Вместо этого:
    QString pageid = "page" + QString("%1").arg(1,2,10,QChar('0'));

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

и все заработало! Отдельное спасибо Kagami за помощь :)
Litkevich Yuriy
Цитата(FladeX @ 13.4.2009, 18:15) *
Отдельное спасибо Kagami за помощь
кни ему "Спасибу"
Kagami
Если бы я заметил что в середине обсуждения у тебя поменялся xml-файл все решилось бы быстрее :)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.