Russian Qt Forum

Qt => Уроки и статьи => Тема начата: xintrea от Декабрь 05, 2008, 01:18



Название: Урок: Как получить XML код из DOM представления
Отправлено: xintrea от Декабрь 05, 2008, 01:18
Как получить XML код из DOM представления

Каждый, кто начинает разбираться с XML в Qt и начинает работать с DOM представлением XML данных, быстро обнаруживает, что в Qt отсутсвуют удобные инструменты для получения XML кода из DOM представления. Практически, получить XML код из DOM представления можно только для документа. А вот чтобы проверить содержимое отдельного узла или элемента, программисту приходится дописывать дополнительный код.

В этом уроке приведена готовая функция, которой можно скармливать любые XML-узлы в виде объектов QDomNode (QDomDocument, QDomElement...). На выходе функция возвращает строку с XML-кодом данного объекта.

В написании такой функции, впринципе, нет ничего сложного. Основная идея в том, чтобы прикрепить узлы (или элементы) к пустому документу, и получить XML-код такого временного документа через метод toString(). Однако воникает проблема - нужно получить пустой документ, у которого не будет ни одного узла (элемента), и прикрепить у нему наши узлы. Дело осложняется тем, что к чистому, только что созданному документу прикрепить узлы невозможно. Помните об этом, когда работаете с DOM-объектами! Обязательно нужно, чтобы документ был ненулевым. А это значит, что в результирующей строке будет расположен (помимо XML-кода переданного DOM-элемента) XML-код DOM-представления, который делает документ ненулевым. А лишний XML-код нам ненужен.

Я много времени потратил на то, чтобы разобраться, в какой момент XML DOM-документ становится "ненулевым", и с ним можно начинать работать. Оказывается, если с помощью документа создать отдельный элемент, например через метод createElement(), то, несмотря на то, что созданный элемент не будет иметь отношения к документу (с помощью которого он всего лишь создан), документ хоть и останется пустым, но будет уже "ненулевым". И с ним можно начинать работать.

Не буду дальше размусоливать проблему, и просто приведу код функции. Её можно просто вставить в свой проект и пользоваться.

Код
C++ (Qt)
// Наша замечательная функция
QString domxmlToString(QDomNode xmldata)
{
// Если узел представляет собой полностью документ
if(xmldata.isDocument())
{
 // Значит этот узел можно напрямую преобразовать
 // в документ, а потом в XML строку
 return xmldata.toDocument().toString();
}
else
{
 // Иначе, узел не является документом, и чтобы получить
 // его текстовое представление, надо узел прикрепить к пустому документу
 QDomDocument tempdoc;
 
 // Создаем элемент-вспомогалку, который нигде не используется, но создание которого
 // влияет на tempdoc, делает этот документ ненулевым, оставляя его пустым
 // (Попробуйте закомментировать эту строку, и посмотрите что получится)
 QDomElement tempelm = tempdoc.createElement("stub");
 
 // Если переданный в функцию элемент содержит данные
 if(!xmldata.isNull())
 {
  // Элемент прикрепляется к пустому (но ненулевому!) документу
  tempdoc.appendChild(xmldata.cloneNode());
 
  // И результирующий DOM документ преобразуется в XML строку
  return tempdoc.toString();
 }
 else return ""; // Если данных в элементе небыло, возвращается пустая строка
}
}

А вот как можно использовать эту функцию:

Код
C++ (Qt)
// Конструируем документ
QDomDocument rectab;
 
QDomElement e1=rectab.createElement("element1");
rectab.appendChild(e1);
 
QDomElement e2=rectab.createElement("element2");
e1.appendChild(e2);
 
QDomElement e22=rectab.createElement("element22");
e22.setAttribute("id","100");
e22.setAttribute("book","C++ for proffesional");
e2.appendChild(e22);
 
QDomElement e3=rectab.createElement("element3");
e1.appendChild(e3);
 
// Проверяем что получилось, можем распечатать полный XML документ
qDebug() << domxmlToString(rectab);
 
// А можем распечатать и часть XML документа, представленную
// в виде QDomNode или других производных классов
qDebug() << domxmlToString(e2);

Данный код выведет в консоль как содержимое полного документа, так и содержимое отдельного элемента

Код:
"<element1>
 <element2>
  <element22 book="C++ for proffesional" id="100" />
 </element2>
 <element3/>
</element1>"

"<element2>
 <element22 book="C++ for proffesional" id="100" />
</element2>"


Проверено на Qt 4.3.0 и 4.4.1, Linux.


Лирическое отступление

Если в функции domxmlToString() мы закомментируем "бессмысленную" строчку
QDomElement tempelm = tempdoc.createElement("stub");
то в момент выполнения программы будет выдано предупреждение
Calling appendChild() on a null node does nothing.
и, соответсвенно, XML код элемента e2 не сгенерируется.


Название: Re: Урок: Как получить XML код из DOM представления
Отправлено: Sergeich от Март 29, 2009, 00:45
Каждый, кто начинает разбираться с XML в Qt и начинает работать с DOM представлением XML данных, быстро обнаруживает, что в Qt отсутсвуют удобные инструменты для получения XML кода из DOM представления. Практически, получить XML код из DOM представления можно только для документа. А вот чтобы проверить содержимое отдельного узла или элемента, программисту приходится дописывать дополнительный код.
Гммм... Учим матчасть:
Код:
QString xml;
QTextStream stream( &xml, QIODevice::WriteOnly );
yourDomNode.save( stream, 4 );


Название: Re: Урок: Как получить XML код из DOM представления
Отправлено: xintrea от Март 29, 2009, 14:18
Код:
QString xml;
QTextStream stream( &xml, QIODevice::WriteOnly );
yourDomNode.save( stream, 4 );

Мда, путь конечно неочевидный. Но он лучше приведенного в уроке хотя бы потому, что не использует хак с "ненулевым" элементом, что меня честногря очень напрягало.

Используя метод, предложенный Сергеичем, функция domxmlToString() выглядит так

Код
C++ (Qt)
// Наша замечательная функция
QString domxmlToString(QDomNode xmldata)
{
// Если узел представляет собой полностью документ
if(xmldata.isDocument())
{
 // Значит этот узел можно напрямую преобразовать.
 // в документ, а потом в XML строку
 return xmldata.toDocument().toString();
}
else
{
 // Иначе узел не является документом, и его нужно обрабатывать по-другому
 
 // Строка, где будет размещен XML код
 QString xmlcode;
 
 // Поток, связаный со строкой, в который будет направляться XML код узла
 QTextStream stream(&xmlcode, QIODevice::WriteOnly);
 
 // XML документ записывается в поток, после чего автоматом окажется в строке xmlcode
 xmldata.save(stream, 1);
 
 return xmlcode;
}
}

Лучше использовать именно этот вариант.


Название: Re: Урок: Как получить XML код из DOM представления
Отправлено: Sergeich от Март 29, 2009, 14:53
Проверка
Код:
if(xmldata.isDocument())
- излишняя, т.к. QDomDocument - наследник QDomNode


Название: Re: Урок: Как получить XML код из DOM представления
Отправлено: xintrea от Март 29, 2009, 15:25
Проверка
Код:
if(xmldata.isDocument())
- излишняя, т.к. QDomDocument - наследник QDomNode

Ну так для QDomDocument есть нативный метод получения XML-кода, его я и использую. А для базового QDomNode такого метода нет, потому используется код в альтернативной ветке.