首页 Qt 学习之路 2 Qt 学习之路 2(62):保存 XML

Qt 学习之路 2(62):保存 XML

9 2.2K

前面几章我们讨论了读取 XML 文档的三种方法。虽然各有千秋,但是 Qt 推荐的是使用QXmlStreamReader。与此同时,许多应用程序不仅需要读取 XML,还需要写入 XML。为此,Qt 同样提供了三种方法:

  1. 使用QXmlStreamWriter
  2. 构造一个 DOM 树,然后掉其save()函数;
  3. 使用QString手动生成 XML。

可以看出,无论我们使用哪种读取方式,这几种写入的方法都与此无关。这是因为 W3C 仅仅定义了如何处理 XML 文档,并没有给出如何生成 XML 文档的标准方法(尽管当我们使用 DOM 方式读取的时候,依旧可以使用同样的 DOM 树写入)。

如同 Qt 所推荐的,我们也推荐使用QXmlStreamWriter生成 XML 文档。这个类帮助我们做了很多工作,比如特殊字符的转义。接下来我们使用QXmlStreamWriter将前面几章使用的 XML 文档生成出来:

QFile file("bookindex.xml");
if (!file.open(QFile::WriteOnly | QFile::Text)) {
    qDebug() << "Error: Cannot write file: "
             << qPrintable(file.errorString());
    return false;
}

QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("bookindex");
xmlWriter.writeStartElement("entry");
xmlWriter.writeAttribute("term", "sidebearings");
xmlWriter.writeTextElement("page", "10");
xmlWriter.writeTextElement("page", "34-35");
xmlWriter.writeTextElement("page", "307-308");
xmlWriter.writeEndElement();
xmlWriter.writeStartElement("entry");
xmlWriter.writeAttribute("term", "subtraction");
xmlWriter.writeStartElement("entry");
xmlWriter.writeAttribute("term", "of pictures");
xmlWriter.writeTextElement("page", "115");
xmlWriter.writeTextElement("page", "224");
xmlWriter.writeEndElement();
xmlWriter.writeStartElement("entry");
xmlWriter.writeAttribute("term", "of vectors");
xmlWriter.writeTextElement("page", "9");
xmlWriter.writeEndElement();
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
if (file.error()) {
    qDebug() << "Error: Cannot write file: "
             << qPrintable(file.errorString());
    return false;
}
// ...

首先,我们以只写方式创建一个文件。然后基于该文件我们创建了QXmlStreamWriter对象。setAutoFormatting()函数告诉QXmlStreamWriter要有格式输出,也就是会有标签的缩进。我们也可以使用QXmlStreamWriter::setAutoFormattingIndent()设置每个缩进所需要的空格数。接下来是一系列以 write 开始的函数。这些函数就是真正输出时需要用到的。注意这些函数以 write 开始,有 Start 和 End 两个对应的名字。正如其名字暗示那样,一个用于写入开始标签,一个用于写入结束标签。writeStartDocument()开始进行 XML 文档的输出。这个函数会写下

<?xml version=\"1.0\" encoding=\"UTF-8\"?>

一行。与writeStartDocument()相对应的是最后的writeEndDocument(),告诉QXmlStreamWriter,这个 XML 文档已经写完。下面我们拿出一段典型的代码:

xmlWriter.writeStartElement("entry");
xmlWriter.writeAttribute("term", "of vectors");
xmlWriter.writeTextElement("page", "9");
xmlWriter.writeEndElement();

显然,这里我们首先写下一个 entry 的开始标签,然后给这个标签一个属性 term,属性值是 of vectors。writeTextElement()函数则会输出一个仅包含文本内容的标签。最后写入这个标签的关闭标签。这段代码的输出结果就是:

<entry term="of vectors">
    <page>9</page>
</entry>

其余部分与此类似,这里不再赘述。这样,我们就输出了一个与前面章节所使用的相同的 XML 文档:

<?xml version="1.0" encoding="UTF-8"?>
<bookindex>
    <entry term="sidebearings">
        <page>10</page>
        <page>34-35</page>
        <page>307-308</page>
    </entry>
    <entry term="subtraction">
        <entry term="of pictures">
            <page>115
            <page>224
        </entry>
        <entry term="of vectors">
            <page>9</page>
        </entry>
    </entry>
</bookindex>

尽管我们推荐使用QXmlStreamWriter生成 XML 文档,但是如果现在已经有了 DOM 树,显然直接调用QDomDocument::save()函数更为方便。在某些情况下,我们需要手动生成 XML 文档,比如通过QTextStream

//!!! Qt4
QTextStream out(&file);
out.setCodec("UTF-8");
out << "<doc>\n"
    << "   <quote>" << Qt::escape(quoteText) << "</quote>\n"
    << "   <translation>" << Qt::escape(translationText)
    << "</translation>\n"
    << "</doc>\n";

//!!! Qt5
QTextStream out(&file);
out.setCodec("UTF-8");
out << "<doc>\n"
    << "   <quote>" << quoteText.toHtmlEscaped() << "</quote>\n"
    << "   <translation>" << translationText.toHtmlEscaped()
    << "</translation>\n"
    << "</doc>\n";

这种办法是最原始的办法:我们直接除了字符串,把字符串拼接成 XML 文档。需要注意的是,quoteText 和 translationText 都需要转义,这是 XML 规范里面要求的,需要将文本中的 <,> 以及 & 进行转义。不过,转义函数在 Qt4 中是Qt::escape(),而 Qt5 中则是QString::toHtmlEscaped(),需要按需使用。

9 评论

rookie 2014年4月14日 - 17:58

樓主想請教您些事
新手初接觸Qt,因為一些需要,需要在QML下對XML檔做分析
想請問QXmlStreamWriter或DOM等方法能用在QML下使用麼?如何使用?
目前有看了些官網的資料,在QtQuick的專案下分析XML檔,
它們是使用XmlListModel的方式,這邊想針對其中的query部份請教您,
以下是官網提供的XML範例

A blog post
Sat, 07 Sep 2010 10:00:01 GMT

Another blog post
Sat, 07 Sep 2010 15:35:01 GMT

解析這種類型的XML沒問題,但目前需要解析的XML長相類似如下

A blog post1
A blog post2
A blog post3

Sat, 07
Sat, 08
Sat, 09

......略, 類似上方

同時需要取出item跟record裡的每一筆資料,原本例子寫法
XmlListModel {
id: xmlModel
source: file.fileUrls //這邊我是按button去開啟XML檔案,再把路徑傳過來
query: "/rss/channel/item"

XmlRole { name: "title"; query: "title/string()" }
XmlRole { name: "pubDate"; query: "pubDate/string()" }
}
但換成新的XML檔時,我卡在query該如何設定,這邊想請樓主給點建議,謝謝!!

回复
rookie 2014年4月14日 - 18:00

糟糕
XML語法沒顯示出來...

回复
rookie 2014年4月14日 - 18:11

樓主因為直接用XML語法所以沒顯示出來
這邊我截圖上傳 請樓主幫看
http://i.imgur.com/qrxx6kb.png
http://i.imgur.com/LfovzGP.png

回复
豆子 2014年4月15日 - 10:38

query 使用的是 XPath。你可以看看 XPath 语法。因为你给的代码片段是演示性质的,节点应该不是真实的位置,而 XPath 是与 XML 文档相关的,所以没有办法给出特别的建议。只好建议先了解下 XPath 的相关内容,或者给出实际文档才好帮助。

回复
rookie 2014年4月15日 - 12:01

感謝樓主的建議
事實上真正需要的跟上面截圖第二張滿類似的,只差在內容文字不同
架構上基本一樣
另外想請問樓主,QML下提供的XmlListModel 是不是只能讀而不能寫?
就是我除了讀XML以外還需要產生新的XML檔。
因為目前確定QML是不能動的,但我自己再看XmlListModel 時發現好像沒提供寫XML檔的功能?
抱歉新手有一堆問題,雖然有去官網看了些QML資料,但有些暫時還是無法理出個頭緒來。
PS 留言審核都要等個半天? 如果方便的話可以email跟樓主您請教麼?

回复
豆子 2014年4月17日 - 14:13

是的,XmlListModel 只能读不能写。因此个人建议仅将 QML 作为前端显示用,数据还是用 C++ 提供。话说直接发邮件也没问题的。可以发到 devbean#outlook.com 或 devbean#devbean.net。

回复
l无光之夜 2016年8月13日 - 18:19

while (!xmlreader.atEnd())
{
xmlreader.readNext();
qDebug()<<123;
}
//读取

//xml文件只有这么简单
Debug里面输出了3次是为什么........

回复
向日夏 2017年5月18日 - 20:54

你好,请问用QDomDocument修改xml文件时,若是将一个元素的文本值设为空,例如设为,则在用QTextStream保存xml时,xml会变为,并且再也不能修改title的nodeValue了,应该怎么解决呢

回复
向日夏 2017年5月18日 - 20:56

设为那里没显示出来,是这样《title》《/title》,则保存时就会变为《title /》

回复

回复 豆子 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2