最近使用qt时。项目要求读取xml,在参考了多位大佬的博客后,再此做个记录,以备后用。
本文参考了两位大佬博客,以下是链接。
转自:(124条消息) Qt浅谈之二十八解析XML文件_乌托邦-优快云博客
转自:(124条消息) 利用 Qt 读取 XML 文件的方法_Ivan 的专栏-优快云博客_qt读取xml
一、简介
- QtXml模块提供了一个读写XML文件的流,解析方法包含DOM和SAX。DOM(Document ObjectModel):将XML文件表示成一棵树,便于随机访问其中的节点,但消耗内存相对多一些。SAX(Simple APIfor XML):一种事件驱动的XML API,接近于底层,速度较快,但不便于随机访问任意节点。
使用XML模块,在.pro文件中添加QT += xml,并加如相应的头文件#include <QDomDocument>或者#include <QXmlStreamReader>。
分析的xml文件:
QDomDocument doc("mydocument");
QFile file("mydocument.xml");
if (!file.open(QIODevice::ReadOnly))
return;
if (!doc.setContent(&file))
{
file.close();
return;
}
file.close();
// print out the element names of all elements that are direct children
// of the outermost element.
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChild();
while (!n.isNull())
{
QDomElement e = n.toElement(); // try to convert the node to an element.
if (!e.isNull())
{
cout << qPrintable(e.tagName()) << endl; // the node really is an element.
}
n = n.nextSibling();
}
二、详解
1、QXmlStreamReader
(1)streamparsexml.h
#ifndef STREAMPARSEXML_H
#define STREAMPARSEXML_H
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QFile>
#include <QMessageBox>
class StreamParseXml
{
public:
StreamParseXml();
~StreamParseXml();
int writeXml();
int readXml();
private:
void parseUserInformation();
QString getValue(const QString &name);
QString getAttribute(const QString &name);
private:
QString fileName;
QXmlStreamReader *reader;
};
#endif // STREAMPARSEXML_H
(2)streamparsexml.cpp
#include <QDebug>
#include "streamparsexml.h"
StreamParseXml::StreamParseXml()
{
fileName = "streamparse.xml";
}
StreamParseXml::~StreamParseXml()
{
}
int StreamParseXml::writeXml()
{
QFile file(fileName);
if(file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QXmlStreamWriter writer(&file);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("COMMAND");
writer.writeTextElement("OBJECT", "USER");
writer.writeTextElement("ACTION", "LOGIN");
writer.writeStartElement("DATA");
writer.writeStartElement("USER");
writer.writeAttribute("NAME", "root");
writer.writeAttribute("PASSWORD", "123456");
writer.writeEndElement();
writer.writeEndElement();
writer.writeEndElement();
file.close();
}
return 0;
}
int StreamParseXml::readXml()
{
if(fileName.isEmpty()) return -2;
QFile *file = new QFile(fileName);
if(!file->open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::information(NULL, QString("title"), QString("open error!"));
return -1;
}
reader = new QXmlStreamReader(file);
while(!reader->atEnd() && !reader->hasError()) {
QXmlStreamReader::TokenType token = reader->readNext();
if(token == QXmlStreamReader::StartDocument) {
continue;
}
if (reader->isStartElement() && reader->name() == "OBJECT") {
QString elementText = reader->readElementText();
if (elementText == "USER") {
parseUserInformation();
break;
}
}
}
if (reader->hasError()) {
qDebug() << reader->errorString();
//QMessageBox::information(NULL, QString("parseXML"), reader->errorString());
}
reader->clear();
delete reader;
reader = NULL;
return 0;
}
void StreamParseXml::parseUserInformation()
{
QString elementString = getValue("ACTION");
if (elementString == "LOGIN") {
while(!reader->atEnd()) {
reader->readNext();
if (reader->name() == "USER") {
QXmlStreamAttributes attributes = reader->attributes();
if(attributes.hasAttribute("NAME")) {
qDebug() << "USER=" << attributes.value("NAME").toString();
}
if(attributes.hasAttribute("PASSWORD")) {
qDebug() << "PASSWORD=" << attributes.value("PASSWORD").toString();
}
}
}
}
}
QString StreamParseXml::getValue(const QString &name)
{
while(!reader->atEnd()) {
reader->readNext();
if (reader->isStartElement() && reader->name() == name) {
return reader->readElementText();
}
}
return "";
}
(3)运行
2.1、QDomDocument
QDomDocument类代表整个的XML文件。概念上讲:它是文档树的根节点,并提供了文档数据的基本访问方法。由于元素、文本节点、注释、指令执行等等不可能脱离一个文档的上下文,所以文档类也包含了需要用来创建这些对象的工厂方法。被创建的节点对象有一个ownerDocument()函数,它将对象与对象常见的文档上下文环境关联起来。DOM类中最常使用的是QDomNode、QDomDocument、QDomElement和QDomText。
解析后的XML文件在内部是通过一个对象树来表示的,对象树可以使用各种QDom类进行访问。所有的QDom类只引用内部树上的对象。一旦最后一个DOM树的QDom对象和QDocument本身被删除掉时,DOM树上的所有内部对象会被删除掉。元素、文本节点等的创建是通过使用类提供的各种工厂方法完成的。使用QDom类的缺省构造函数只会生成空的对象,这些空的对象不能操作,也不能写入到文档中。
QDomDocument::setContent()完成XML文档的设置,他从QFile对象中读取XML数据并检测XML文档的编码。setContent()有几种重载形式,可以分别从QByteArray、QString、QIODevice、QXmlInputSource中读取XML数据。
(1)domdocument.h
#ifndef DOMDOCUMENT_H
#define DOMDOCUMENT_H
#include <QDomDocument>
#include <QFile>
#include <QTextStream>
class DomDocument
{
public:
DomDocument();
~DomDocument();
int writeXml();
int readXml();
int readXml2();
private:
QString fileName;
};
#endif // DOMDOCUMENT_H
(2)domdocument.cpp
#include <QDebug>
#include "domdocument.h"
DomDocument::DomDocument()
{
fileName = "domparse.xml";
}
DomDocument::~DomDocument()
{
}
int DomDocument::writeXml()
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
return -2;
QTextStream out(&file);
QDomDocument doc;
QDomText text;
QDomElement element;
QDomAttr attr;
QDomProcessingInstruction instruction;
instruction = doc.createProcessingInstruction( "xml", "version = \'1.0\' encoding=\'UTF-8\'" );
doc.appendChild( instruction );
QDomElement root = doc.createElement( "COMMAND" );
doc.appendChild(root);
element = doc.createElement( "OBJECT" );
text = doc.createTextNode( "USER" );
element.appendChild(text);
root.appendChild(element);
element = doc.createElement( "ACTION" );
text = doc.createTextNode( "LOGIN" );
element.appendChild(text);
root.appendChild(element);
element = doc.createElement( "DATA" );
root.appendChild(element);
QDomElement userElement = doc.createElement( "USERINFO" );
attr = doc.createAttribute( "NAME" );
attr.setValue("root");
userElement.setAttributeNode(attr);
attr = doc.createAttribute( "PASSWORD" );
attr.setValue("123456");
userElement.setAttributeNode(attr);
element.appendChild(userElement);
doc.save(out, 4); //each line space of file is 4
return 0;
}
int DomDocument::readXml()
{
QDomDocument doc;
QFile file(fileName);
QString error = "";
int row = 0, column = 0;
if (!file.open(QIODevice::ReadOnly)) return -2;
if(!doc.setContent(&file, false, &error, &row, &column)){
qDebug() << "parse file failed:" << row << "---" << column <<":" <<error;
file.close();
return -1;
}
file.close();
QDomElement root = doc.documentElement();
QDomNode node = root.firstChild();
while(!node.isNull()) {
QDomElement element = node.toElement(); // try to convert the node to an element.
if(!element.isNull()) {
qDebug()<<element.tagName() << ":" << element.text();
QDomNode nodeson = element.firstChild();
while(!nodeson.isNull()) {
QDomElement elementson = nodeson.toElement();
if(!elementson.isNull()) {
qDebug()<< "---" <<elementson.tagName();
if (elementson.hasAttribute("NAME")) {
qDebug()<< "---" << "NAME=" << elementson.attributeNode("NAME").value();
}
if (elementson.hasAttribute("PASSWORD")) {
qDebug()<< "---" << "PASSWORD=" << elementson.attributeNode("PASSWORD").value();
}
}
nodeson = nodeson.nextSibling();
}
}
node = node.nextSibling();
}
return 0;
}
int DomDocument::readXml2()
{
QDomDocument doc;
QFile file(fileName);
QString error = "";
int row = 0, column = 0;
if (!file.open(QIODevice::ReadOnly)) return -2;
if(!doc.setContent(&file, false, &error, &row, &column)){
qDebug() << "parse file failed:" << row << "---" << column <<":" <<error;
file.close();
return -1;
}
file.close();
QDomElement root = doc.documentElement();
QDomNode node = root.firstChildElement();
while(!node.isNull()) {
QDomElement element = node.toElement(); // try to convert the node to an element.
if(!element.isNull()) {
if (element.tagName() == "DATA") {
qDebug()<< "---" <<element.tagName();
QDomNodeList list = element.childNodes();
for(int index = 0; index < list.count(); index++) {
QDomNode list_node = list.item(index);
QDomElement list_element = list_node.toElement();
if (list_element.hasAttribute("NAME")) {
qDebug()<< "---" << "NAME =" << list_element.attributeNode("NAME").value();
}
if (list_element.hasAttribute("PASSWORD")) {
qDebug()<< "---" << "PASSWORD =" << list_element.attributeNode("PASSWORD").value();
}
}
}
else {
qDebug()<<element.tagName() << ":" << element.text();
}
}
node = node.nextSibling();
}
return 0;
}
(3)main.cpp
#include <QCoreApplication>
#include <QDebug>
#include "streamparsexml.h"
#include "domdocument.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// StreamParseXml stream;
// stream.writeXml();
// stream.readXml();
DomDocument dom;
dom.writeXml();
dom.readXml();
qDebug() << "***********************";
dom.readXml2();
return a.exec();
}
(4)运行
2.2、QDomDocument
转自:(124条消息) 利用 Qt 读取 XML 文件的方法_Ivan 的专栏-优快云博客_qt读取xml
XML 是可扩展标记语言(Extensible Markup Language)的缩写。XML 文件由内容和标记组成,通过以标记包围内容的方式将大部分内容包含在元素中。
Qt 中提供了多种读取XML文件的方法,这里简单的记录一下用 QDomDocument 读取的步骤。为什么使用QDomDocument 呢,因为XML 本身就是一以树状结构组织数据的,而DOM 也是将数据组织为树状结构,最适合直观地展示XML数据。
下面的代码是Qt 帮助文件中自带的例子代码:
QDomDocument doc("mydocument");
QFile file("mydocument.xml");
if (!file.open(QIODevice::ReadOnly))
return;
if (!doc.setContent(&file))
{
file.close();
return;
}
file.close();
// print out the element names of all elements that are direct children
// of the outermost element.
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChild();
while (!n.isNull())
{
QDomElement e = n.toElement(); // try to convert the node to an element.
if (!e.isNull())
{
cout << qPrintable(e.tagName()) << endl; // the node really is an element.
}
n = n.nextSibling();
}
如果xml有多层,那么可以递归的去读取。我写了小程序将xml 的的数据读入到一个树型列表控件中。下面是核心的代码:
#ifndef DIALOG_H
#define DIALOG_H
#include <QtXml>
#include <QDialog>
namespace Ui {
class Dialog;
}
class QTreeWidgetItem;
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
void listDom(QDomElement& docElem, QTreeWidgetItem* pItem);
void openXML(QString fileName);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void openFile();
};
#endif // DIALOG_H
#include "dialog.h"
#include "ui_dialog.h"
#include <QFileDialog>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButtonOpen, SIGNAL(clicked()), this, SLOT(openFile()));
ui->treeWidget->setColumnCount(2);
ui->treeWidget->setColumnWidth(0, 400);
setWindowFlags(Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint);
showMaximized();
}
void Dialog::openXML(QString fileName)
{
QFile file(fileName);
if(file.open(QIODevice::ReadOnly))
{
QDomDocument dom("WCM");
if (dom.setContent(&file))
{
ui->treeWidget->clear();
QDomElement docElem = dom.documentElement();
listDom(docElem, NULL);
}
}
file.close();
}
void Dialog::openFile()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open XML File"), "c:/", tr("XML Files (*.xml)"));
if(!fileName.isEmpty())
{
openXML( fileName );
}
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::listDom(QDomElement& docElem, QTreeWidgetItem * pItem)
{
QDomNode node = docElem.firstChild();
if(node.toElement().isNull())
{
pItem->setText (1, docElem.text());
}
while(!node.isNull())
{
QDomElement element = node.toElement(); // try to convert the node to an element.
if( !element.isNull() )
{
QTreeWidgetItem *item;
if( pItem )
{
item = new QTreeWidgetItem(pItem);
}
else
{
item = new QTreeWidgetItem(ui->treeWidget);
}
item->setText(0, element.tagName());
listDom(element, item);
if( pItem )
{
pItem->addChild(item);
}
else
{
ui->treeWidget->addTopLevelItem(item);
}
}
node = node.nextSibling();
}
return;
}
下面是个测试 xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<recipe type="dessert">
<recipename cuisine="american" servings="1">Ice Cream Sundae</recipename>
<ingredlist>
<listitem>
<quantity units="cups">0.5</quantity>
<itemdescription>vanilla ice cream</itemdescription>
</listitem>
<listitem>
<quantity units="tablespoons">3</quantity>
<itemdescription>chocolate syrup or chocolate fudge</itemdescription>
</listitem>
<listitem>
<quantity units="tablespoons">1</quantity>
<itemdescription>nuts</itemdescription>
</listitem>
<listitem>
<quantity units="each">1</quantity>
<itemdescription>cherry</itemdescription>
</listitem>
</ingredlist>
<utensils>
<listitem>
<quantity units="each">1</quantity>
<utensilname>bowl</utensilname>
</listitem>
<listitem>
<quantity units="each">1</quantity>
<utensilname>spoons</utensilname>
</listitem>
<listitem>
<quantity units="each">1</quantity>
<utensilname>ice cream scoop</utensilname>
</listitem>
</utensils>
<directions>
<step>Using ice cream scoop, place vanilla ice cream into bowl.</step>
<step>Drizzle chocolate syrup or chocolate fudge over the ice cream.</step>
<step>Sprinkle nuts over the mound of chocolate and ice cream.</step>
<step>Place cherry on top of mound with stem pointing upward.</step>
<step>Serve.</step>
</directions>
<variations>
<option>Replace nuts with raisins.</option>
<option>Use chocolate ice cream instead of vanilla ice cream.</option>
</variations>
<preptime>5 minutes</preptime>
</recipe>
下面是软件界面:
3、其他XML解析库
常见C/C++ XML解析器有tinyxml、XERCES、squashxml、xmlite、pugxml、libxml等等,这些解析器有些是支持多语言的,有些只是单纯C/C++的。
(1)Xerces XML解析器
官方网址:http://xerces.apache.org/xerces-c/
Xerces前身是IBM的XML4C,XML4C也是一种功能强大的XML解析器,之后交给Apache基金会管理,遂改名为Xerces,Xerces-C++让你的程序提供读写XML数据更加容易,提供的共享库通过DOM、SAX、SAX2 API等方式对XML文档进行解析、生成、操作和验证。
Xerces-C++忠实于XML 1.0建议和相关标准。
Xerces-C++解析器高性能、模块化并且可扩展。相关开发资料也比较完善。
除了C++版本,Xerces同时还提供Xerces Java,Xerces Perl等版本。
(2)TinyXML解析器
官方网址:http://www.grinninglizard.com/tinyxml/
TinyXML相比Xerces要功能简单些,正如其名Tiny,使用方法也比较简单,TinyXML也是一个开源的解析XML解析库,用于C++,支持Windows和Linux。TinyXML通过DOM模型遍历和分析XML。官方文档:
http://www.grinninglizard.com/tinyxmldocs/index.html
(3)squashXML解析器
官方地址:http://ostatic.com/squashxml
这个解析器在国内似乎少人使用,这个解析器也有些历史了。squashXML基于DOM Level2,也是一个XML轻量级的解析器。天缘之所以把这个写上是天缘比较看重这个解析器的目录划分及使用说明,易懂而且易上手。
(4)XMLBooster解析器
官方网址:http://xerces.apache.org/xerces-c/
XMLBooster开发关注点比较有特色,更加关注解析性能,声称:“Application integration of XML data cannot get any simpler or any faster: instead of dealing with sophisticated api (such as DOM or SAX), use a convenient data structure, generated to suit your specific purpose, in the language of your choice. ”。
针对特殊需求使用更加方便的数据结构以提高性能。
(5)LibXML解析器
官方地址:http://xmlsoft.org/
LibXML本来是为Gnome项目开发(C开发),之后被广泛使用,功能非常强大,几乎适合于常见的所有操作系统下编译和开发使用。libxml++(地址:http://libxmlplusplus.sourceforge.net/)是对libxml XML解析器的C++封装版本。此外还有各种语言封装包,参考官方链接。
(6)补充:
除了上述XML解析库外,还有一些XML解析器(参考:http://www.garshol.priv.no/xmltools/platform/cpp.html),比如Berkely DBXML(BDB)等,有兴趣的读者可自行Google搜索。
尽管XML解析器有很多种,而且功能差异很大,甚至是支持跨平台、多语言,但是对于你的应用而言,尽量选择一种相对熟悉、功能够用的即可,没必要去追求庞杂的解析器,我们只需关注:功能够用、相对稳定、适合扩展这三个功能即可。一旦有问题,修正和扩展都要更为容易。
四、总结
(1)注意:readElementText()函数在解析出开始标签时,就可以解析元素的文本了,直到遇到结束标签。readElementText()函数中好像有类似于readNext()这样的函数读取下一个元素并比较是否为endElement,所以当使用readElementText()读取完元素文本时,该元素的闭合标签已经被读走了。
(2)QDomProcessingInstruction instruction;instruction = doc.createProcessingInstruction("xml","version=/"1.0/" encoding=/"UTF-8/"");用来写入XML文件的声明,这对于一个XML文件来说不可缺少。
(3)源码已经打包上传到csdn上,可登录下载(http://download.youkuaiyun.com/detail/taiyang1987912/8857327)。
(4)若有建议,请留言,在此先感谢!