Russian Qt Forum

Qt => XML => Тема начата: izhack от Март 06, 2013, 17:22



Название: XML парсер
Отправлено: izhack от Март 06, 2013, 17:22
Есть .xml файлик:
Код:
<?xml version="1.0"?>
<XMLBIBLE biblename="Russian">
    <BIBLEBOOK bname="Genesis">
        <CHAPTER cnumber="1">
            <VERS vnumber="1">In the beginning God created the heavens and the earth.</VERS>
        </CHAPTER>
    </BIBLEBOOK>
</XMLBIBLE>
Не могу получить аттрибуты тега "BIBLEBOOK", зато аттрибуты "XMLBIBLE" считываются.
Код:
QMap<QString, QString> Bible::parseBook(QXmlStreamReader& xml) 
{
    QMap<QString, QString> xmlbible;
    QXmlStreamAttributes attributes = xml.attributes();
    if(xml.tokenType() != QXmlStreamReader::StartElement && xml.name() == "XMLBIBLE")
    {
        return xmlbible;
     }
    if(attributes.hasAttribute("biblename"))
   {
        xmlbible["biblename"] = attributes.value("biblename").toString();
    }
    if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == "BIBLEBOOK")
    {
        if(attributes.hasAttribute("bname"))
    {
        xmlbible["bname"] = attributes.value("bname").toString();
    }
    }
    xml.readNext();
    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "XMLBIBLE"))
    {
        if(xml.tokenType() == QXmlStreamReader::StartElement)
       {   
            /*название книги*/
            if(xml.name() == "BIBLEBOOK")
        {
                this->addElementDataToMap(xml, xmlbible);
            }
            /*номер главы*/
            if(xml.name() == "CHAPTER")
        {
                this->addElementDataToMap(xml, xmlbible);
            }   
            /*номер текста*/
            if(xml.name() == "VERS")
        {
                this->addElementDataToMap(xml, xmlbible);
             }
        }
        xml.readNext();
    }
    return xmlbible;
}


Название: Re: XML парсер
Отправлено: ViTech от Март 07, 2013, 10:52
Добавьте вывод отладочных сообщений после выполнения readNextStartElement, readNext и т.п., чтобы посмотреть текущее имя элемента и тип токена. Тогда станет понятнее, в каком месте xml-файла находится парсер и что делать дальше.

Можно еще подумать, нужно ли парсить этот файл во внутренние структуры, или воспользоваться отображением всего файла в QDomDocument. Это зависит от поставленной задачи.


Название: Re: XML парсер
Отправлено: izhack от Март 20, 2013, 16:33
Вроде понял в чем ошибка. Но не могу разобраться с алгоритмом обработки .xml файла QXmlStreamReader'ом.
В моём файле большая "вложенность" элементов. Поэтому парсер находит первый элемент, т.е.
Код
XML
<XMLBIBLE biblename="Russian">
, и его конечный элемент
Код
XML
</XMLBIBLE>
.
и читает его. А остальные элементы он не видит, т.к. достиг конечного элемента.
Попытался выполнит такой цикл
Код
C++ (Qt)
while(!(token == QXmlStreamReader::EndElement && xml.name() == "XMLBIBLE"))
{
if(token == QXmlStreamReader::StartElement && xml.name() == "XMLBIBLE")
{
books.append(this->parseBook(xml));
}
xml.readNext();
       }
для всех элементов. Он не работает. Как проверить, что для именно этого StartElement'а есть соответствующий ему EndElement, и парсер смог пробежаться по всем элементам?

Вот класс parseXML()
Код
C++ (Qt)
void Bible::parseXML()
{
   QFile* file = new QFile("C:\\Users\\Adm\\Documents\\Visual Studio 2008\\Projects\\Project1\\Bible\\Win32\\Debug\\russian.xml");
   if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
   {
       QMessageBox::critical(this, "Bible::parseXML", "Couldn't open russian.xml", QMessageBox::Ok);
       return;
   }
   QXmlStreamReader xml(file);
   QList< QMap<QString,QString> > books;
   while(!xml.atEnd() && !xml.hasError())
   {
       QXmlStreamReader::TokenType token = xml.readNext();
       if(token == QXmlStreamReader::StartDocument)
       {
           continue;
       }
       if(token == QXmlStreamReader::StartElement)
       {
           if(xml.name() == "XMLBIBLE")
           {
               books.append(this->parseBook(xml));
           }
 
           if(xml.name() == "BIBLEBOOK")
           {
               books.append(this->parseBook(xml));
           }
 
           if(xml.name() == "CHAPTER")
           {
               books.append(this->parseBook(xml));
           }
           if(xml.name() == "VERS")
           {
               books.append(this->parseBook(xml));
           }
       }
   }
   if(xml.hasError())
   {
       QMessageBox::critical(this, "Bible::parseXML", xml.errorString(), QMessageBox::Ok);
   }
   xml.clear();
   this->addBooksToUI(books);
}


Название: Re: XML парсер
Отправлено: sergek от Март 22, 2013, 10:37
Вроде понял в чем ошибка. Но не могу разобраться с алгоритмом обработки .xml файла QXmlStreamReader'ом.
В моём файле большая "вложенность" элементов. Поэтому парсер находит первый элемент, т.е.
...
и читает его. А остальные элементы он не видит, т.к. достиг конечного элемента.
Немного не так. Парсер, встречая открывающий или закрывающий тег, вызывает соответствующий обработчик. А ваша задача следить за уровнем вложенности и считывать выдаваемые им атрибуты.

Ранее я уже пытался найти способ упрощения работы с xml-документами с помощью sax - во вложении реализация этого способа в приложении к вашему случаю (Qt 5.0.1).

Более подробно можно почитать здесь: http://citforum.ru/internet/xml/objectview/ (http://citforum.ru/internet/xml/objectview/). Правда, есть отличия в обработке текстовых элементов - я избавился от виртуального метода isTextElement. Собственно, меня ваш пример заинтересовал именно тем, что в у вас в текстовом элементе используются атрибуты. Я раньше на это не закладывался  :(, потом статью доработаю.

Объем кода примера вас не должен пугать - меняется только код xmldocs.cpp (реализация объектного представления документа bible.xml) и mainwindow.cpp (пример использования), остальное, т.е. cnode.cpp, cxmlreader.cpp - во всех проектах неизменно.

Кстати, можете оценить объем кода, рожденного C++Builder, с использованием dom (bible.cpp).


Название: Re: XML парсер
Отправлено: izhack от Март 22, 2013, 11:10
Спасибо гигантское! Буду разбираться.
Кстати удалось получить атрибуты, хоть и криво но всё же:
Код
C++ (Qt)
while(!xml.atEnd())
{
QXmlStreamReader::TokenType token = xml.readNext();
 
if(token == QXmlStreamReader::StartDocument)
{
continue;
}
 
       if(token == QXmlStreamReader::StartElement)
{
QXmlStreamAttributes attributes = xml.attributes();
 
if(xml.name() == "XMLBIBLE")
{
if(attributes.hasAttribute("biblename"))
{
xmlbible["biblename"] = attributes.value("biblename").toString();
}
           }
}
 
if(token == QXmlStreamReader::StartElement)
{
QXmlStreamAttributes attributes = xml.attributes();
if(xml.name() == "BIBLEBOOK")
{
if (attributes.hasAttribute("bname"))
{
xmlbible["bname"] = attributes.value("bname").toString();
}
}
}
 
       if(token == QXmlStreamReader::StartElement)
{
QXmlStreamAttributes attributes = xml.attributes();
if(xml.name() == "CHAPTER")
{
if(attributes.hasAttribute("cnumber"))
{
xmlbible["cnumber"] = attributes.value("cnumber").toString();
}
}
}
if(token == QXmlStreamReader::StartElement)
{
QXmlStreamAttributes attributes = xml.attributes();
if(xml.name() == "VERS")
{
if(attributes.hasAttribute("vnumber"))
{
xmlbible["vnumber"] = attributes.value("vnumber").toString();
}
           }
}
xml.readNext();
   }


Название: Re: XML парсер
Отправлено: sergek от Март 22, 2013, 11:18
А теперь представьте, что у вас не по 1 атрибуту в элементе, а по 100. И как все это будет работать?  :)


Название: Re: XML парсер
Отправлено: izhack от Март 22, 2013, 12:40
А теперь представьте, что у вас не по 1 атрибуту в элементе, а по 100. И как все это будет работать?  :)
Согласен.
Поэтому читаю Ваш код. :)