写入XML
能读取XML文件的大多数应用程序也需要写人XML文件。一般来说,主要有三种由Qt应用程序生成XML文件的方法:
● 使用QXmIStreamWriter
● 构建DOM树并对它调用save()
● 手动生成XML
实际上,这三种方法的选取通常与是否使用QXmlStreamWriter、DOM或SAX来读取XML文档并无关系,尽管数据保留在DOM树中时直接保存这个DOM树是完全说得通的。
利用QXmlStreamWrite类来写入XML文件非常容易,因为它将时刻为我们关注那些特殊的转义字符。如果想利用QXmlStreamWrite从QTreeWidget中输出书刊索引数据,只需要使用两个函数。第一个函数获得文件名和一个QTreeWidget*
,并且将遍历树中所有的顶级项:
bool writeXml(const QString &fileName, QTreeWidget *treeWidget)
{
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
std::cerr << "Error: Cannot write file "
<< qPrintable(fileName) << ": "
<< qPrintable(file.errorString()) << std::endl;
return false;
}
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("bookindex");
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i)
writeIndexEntry(&xmlWriter, treeWidget->topLevelItem(i));
xmlWriter.writeEndDocument();
file.close();
if (file.error()) {
std::cerr << "Error: Cannot write file "
<< qPrintable(fileName) << ": "
<< qPrintable(file.errorString()) << std::endl;
return false;
}
return true;
}
如果开启了自动格式编辑功能,XML文档将以更友好易读的格式输出,在每一行都有明确显示数据递归结构的缩进。writeStartDocument()函数则写下XML文档首行:
<?xml version-"1.0* encoding="UTF-8"?>
writeStartElement()函数随给定的标签文本生成一个新的开始标签。writeStartDocument()函数则关闭任何打开的开始标签。对于每一个顶级项,我们调用writeIndexEntry()函数,并将QXmlStreamWriter和要输出的项传递给writeIndexEntry()函数。下面是writeIndexEntry()的代码:
void writeIndexEntry(QXmlStreamWriter *xmlWriter, QTreeWidgetItem *item)
{
xmlWriter->writeStartElement("entry");
xmlWriter->writeAttribute("term", item->text(0));
QString pageString = item->text(1);
if (!pageString.isEmpty()) {
QStringList pages = pageString.split(", ");
foreach (QString page, pages)
xmlWriter->writeTextElement("page", page);
}
for (int i = 0; i < item->childCount(); ++i)
writeIndexEntry(xmlWriter, item->child(i));
xmlWriter->writeEndElement();
}
writeIndexEntry()函数创建一个对应于QTreeWidgetltem的<entry>
元素并将其作为参数接收。 writeAttribute()函数则为刚刚写入的标签添加一个属性。例如,它可以将<entry>
转变为<entry tenr="sidebearings">
。如果其中有页码,那么页码将以逗号-空格分隔,并且对于每一个页码数,会有一个单独的<page>...</page>
标签对与其间的页码文本一起被写入。这些都可以通过调用writeTextElement()以及将标签名以及开始和结束标签之间的文本传递给writeTextElement()来实现。在所有的情况中XmlStreamWriter都考虑了特殊的XML转义字符的处理,我们不必为此担心。
如果项中有子项就对每一个子项递归调用writelndexEntry()。最后我们调用writeEndElement()来输出</entry>
使用QXmlStreamWirter是写入XML文档最容易最安全的方式但如果已经在一个DOM树中有XML文档的话,则只需要在QDomDocument对象上调用save()函数并要求这个DOM树输出相关的XML即可。默认情况下save()使用UTF8作为所生成文件的编码方式。我们可以通过为DOM树预先进行<?xml?>声明,而使用其他种类的编码方式,例如:
<?xml version="1.0" encoding="IS0-8859-1"?>
下面的代码段给出了其完成过程:
const int Indent = 4;
QDomDocument doc;
...
QTextStream out(&file);
QDomNode xmlNode = doc.createProcessingInstruction("xml", "version=\"1.0\" enconding=\"ISO-8895-1\"");
doc.insertBefore(xmlNode, doc.firstChild());
doc.save(out, Indent);
从Qt4.3开始一个替代的方式就是利用setCodec()对 QTextStream设置编码,并将 QDomNode::EncodingFromTextStream作为第三个参数传递给save()。
手动生成XML文件并不比利用DOM生成XML文件更加困难。我们可以使用QTextStream并且写入字符串就像写入其他任何文本文件一样。最需要慎重对待的部分就是转义在文本和属性值中的特殊字符。Qt::escape()函数可以转义“<”“>”和“&"等特殊字符。以下是一个使用它的代码:
QTextStream out(&file);
out.setCodec("UTF-8");
out << "<doc>\n"
<< " <quote>" << Qt::escape(quoteText) << "</quote>\n"
<<" <translation>" << Qt::escape(translationText)
<< "</translation>\n"
<< "</doc>\n";
当采用这样的方式生成 XML 文件时,除了必须写入正确的 <?xml?> 声明并设置正确的编码外,还必须记得对写入的文本进行转义。如果使用了属性值,还必须转义其中的单引号或双引号。不过,使用 XmlStreamWriter 就会简单得多了,因为它将为我们处理上述所需注意的一切。
xmlstremwriter.cpp
#include <QtGui>
#include <QtXml>
#include <iostream>
void populateTree(QTreeWidget *treeWidget)
{
QStringList labels;
labels << QObject::tr("Terms") << QObject::tr("Pages");
treeWidget->setHeaderLabels(labels);
treeWidget->header()->setResizeMode(QHeaderView::Stretch);
treeWidget->setWindowTitle(QObject::tr("XML Stream Writer"));
treeWidget->show();
(void) new QTreeWidgetItem(treeWidget,
QStringList() << "sidebearings" << "10, 34-35, 307-308");
QTreeWidgetItem *subterm = new QTreeWidgetItem(treeWidget,
QStringList() << "subtraction");
(void) new QTreeWidgetItem(subterm,
QStringList() << "of pictures" << "115, 244");
(void) new QTreeWidgetItem(subterm,
QStringList() << "of vectors" << "9");
}
void writeIndexEntry(QXmlStreamWriter *xmlWriter, QTreeWidgetItem *item)
{
xmlWriter->writeStartElement("entry");
xmlWriter->writeAttribute("term", item->text(0));
QString pageString = item->text(1);
if (!pageString.isEmpty()) {
QStringList pages = pageString.split(", ");
foreach (QString page, pages)
xmlWriter->writeTextElement("page", page);
}
for (int i = 0; i < item->childCount(); ++i)
writeIndexEntry(xmlWriter, item->child(i));
xmlWriter->writeEndElement();
}
bool writeXml(const QString &fileName, QTreeWidget *treeWidget)
{
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
std::cerr << "Error: Cannot write file "
<< qPrintable(fileName) << ": "
<< qPrintable(file.errorString()) << std::endl;
return false;
}
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("bookindex");
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i)
writeIndexEntry(&xmlWriter, treeWidget->topLevelItem(i));
xmlWriter.writeEndDocument();
file.close();
if (file.error()) {
std::cerr << "Error: Cannot write file "
<< qPrintable(fileName) << ": "
<< qPrintable(file.errorString()) << std::endl;
return false;
}
return true;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTreeWidget treeWidget;
populateTree(&treeWidget);
writeXml("out1.xml", &treeWidget);
return app.exec();
}