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

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

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

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

    1. QFile file("bookindex.xml");
    2. if (!file.open(QFile::WriteOnly | QFile::Text)) {
    3. qDebug() << "Error: Cannot write file: "
    4. << qPrintable(file.errorString());
    5. return false;
    6. }
    7.  
    8. QXmlStreamWriter xmlWriter(&file);
    9. xmlWriter.setAutoFormatting(true);
    10. xmlWriter.writeStartDocument();
    11. xmlWriter.writeStartElement("bookindex");
    12. xmlWriter.writeStartElement("entry");
    13. xmlWriter.writeAttribute("term", "sidebearings");
    14. xmlWriter.writeTextElement("page", "10");
    15. xmlWriter.writeTextElement("page", "34-35");
    16. xmlWriter.writeTextElement("page", "307-308");
    17. xmlWriter.writeEndElement();
    18. xmlWriter.writeStartElement("entry");
    19. xmlWriter.writeAttribute("term", "subtraction");
    20. xmlWriter.writeStartElement("entry");
    21. xmlWriter.writeAttribute("term", "of pictures");
    22. xmlWriter.writeTextElement("page", "115");
    23. xmlWriter.writeTextElement("page", "224");
    24. xmlWriter.writeEndElement();
    25. xmlWriter.writeStartElement("entry");
    26. xmlWriter.writeAttribute("term", "of vectors");
    27. xmlWriter.writeTextElement("page", "9");
    28. xmlWriter.writeEndElement();
    29. xmlWriter.writeEndElement();
    30. xmlWriter.writeEndDocument();
    31. file.close();
    32. if (file.error()) {
    33. qDebug() << "Error: Cannot write file: "
    34. << qPrintable(file.errorString());
    35. return false;
    36. }
    37. // ...

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

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

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

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

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

    1. <entry term="of vectors">
    2. <page>9</page>
    3. </entry>

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

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <bookindex>
    3. <entry term="sidebearings">
    4. <page>10</page>
    5. <page>34-35</page>
    6. <page>307-308</page>
    7. </entry>
    8. <entry term="subtraction">
    9. <entry term="of pictures">
    10. <page>115
    11. <page>224
    12. </entry>
    13. <entry term="of vectors">
    14. <page>9</page>
    15. </entry>
    16. </entry>
    17. </bookindex>

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

    1. //!!! Qt4
    2. QTextStream out(&file);
    3. out.setCodec("UTF-8");
    4. out << "<doc>\n"
    5. << " <quote>" << Qt::escape(quoteText) << "</quote>\n"
    6. << " <translation>" << Qt::escape(translationText)
    7. << "</translation>\n"
    8. << "</doc>\n";
    9.  
    10. //!!! Qt5
    11. QTextStream out(&file);
    12. out.setCodec("UTF-8");
    13. out << "<doc>\n"
    14. << " <quote>" << quoteText.toHtmlEscaped() << "</quote>\n"
    15. << " <translation>" << translationText.toHtmlEscaped()
    16. << "</translation>\n"
    17. << "</doc>\n";

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