qt解析xml(1)-----QXmlStreamReader
XML
l XML(eXtensible Markup Language,可扩展标记语言)是普通用于数据交换和数据存储的一种多用途文本文件格式;SVG(可标量矢量图形)XML格式,QtSvg模块提供了可用于载入并呈现SVG图像的类;
l MathML(数学标记语言)XML格式的绘制文档,可以使用Qt Solution中的QtMmlWidget操作;
l 由万维网协会开发。
Qt对XML数据处理的三种方式:
1、 QXmlSreamReader:
QXmlStreamReader:一种快速的基于流的方式访问良格式 XML 文档,特别适合于实现一次解析器(所谓“一次解析器”,可以理解成我们只需读取文档一次,然后像一个遍历器从头到尾一次性处理 XML 文档,期间不会有反复的情况,也就是不会读完第一个标签,然后读第二个,读完第二个又返回去读第一个,这是不允许的);
2、 DOM文档对象模型
将整个 XML 文档读入内存,构建成一个树结构,允许程序在树结构上向前向后移动导航,这是与另外两种方式最大的区别,也就是允许实现多次解析器(对应于前面所说的一次解析器)。DOM 方式带来的问题是需要一次性将整个 XML 文档读入内存,因此会占用很大内存;
3、 SAX XML简单应用程序编程接口
提供大量虚函数,以事件的形式处理 XML 文档。这种解析办法主要是由于历史原因提出的,为了解决 DOM 的内存占用提出的(在现代计算机上,这个一般已经不是问题了)。
1、使用QXmlStreamReader读取XML
适用于诸如查找XML文档中一个给定的标记符出现的次数,读取内容容纳不了的特大文件,组装定制的数据结构以反映XML文档的内容等。
在.pro文件中添加QT += xml,并加如相应的头文件#include <QDomDocument>或者#include <QXmlStreamReader>。
流读取器的基本概念是将XML文档报告为tokens,类似于SAX。 QXmlStreamReader和SAX之间的主要区别是如何报告这些XML tokens。使用SAX,应用程序必须提供处理程序(回调函数),在解析器方便时从解析器接收所谓的XML事件。使用QXmlStreamReader,应用程序代码本身驱动循环,并从读取器,一个接一个地,因为它需要它们。这通过调用readNext()来完成,读取器从输入流读取,直到它完成下一个token,此时它返回tokenType()。然后可以使用一组方便的函数,包括isStartElement()和text()来检查token以获取有关已读取的信息。这种拉方法的最大优点是可以使用它构建递归下降解析器,这意味着您可以将XML解析代码轻松地分成不同的方法或类。这使得在解析XML时可以轻松跟踪应用程序自己的状态。
各节点获取方式
<?xml version="1.0" encoding="UTF-8"?>
<COMMAND>
<OBJECT>USER</OBJECT>
<ACTION>LOGIN</ACTION>
<DATA>
<USER NAME="root" PASSWORD="123456"/>
</DATA>
</COMMAND>
对应各节点获取方法
“ACTION”: 使用Name:m_pReader->name()
“LOGIN”:对应ElementText使用:m_pReader->readElementText();
“USER”:Name
“NAME”和“PASSWORD”: QXmlStreamAttributes m_pReader->attributes(); 可以使用attributes.hasAttribute("NAME")判断是否有节点,attributes.value("NAME").toString();读取其值。
readNext:读取下一个token :返回QXmlStreamReader::TokenType,可于判断节点类型。
isStartElement():是否是开始节点
readElementText();获取数据
如果在解析时发生错误,atEnd()和hasError()返回true,而error()返回发生的错误。 函数errorString(),lineNumber(),columnNumber()和characterOffset()用于构造相应的错误或警告消息。 为了简化应用程序代码,QXmlStreamReader包含一个raiseError()机制,让您提出自定义错误,触发同样的错误处理描述。
增量解析
QXmlStreamReader是一个增量解析器。它可以处理文档不能一次解析的情况,因为它到达块(例如从多个文件或通过网络连接)。当读取器在完整文档被解析之前缺少数据时,它报告一个PrematureEndOfDocumentError。当更多的数据到达时,由于调用addData()或者由于通过网络设备()有更多的数据可用,阅读器从PrematureEndOfDocumentError错误中恢复,并继续下一次调用readNext()来解析新的数据。
例如,如果您的应用程序使用网络访问管理器从网络读取数据,您将向管理器发出网络请求,并作为回应接收网络回复。由于QNetworkReply是一个QIODevice,您将其readyRead()信号连接到自定义插槽,例如。 slotReadyRead()在QNetworkAccessManager的讨论中显示的代码片段。在这个插槽中,您使用readAll()读取所有可用数据,并使用addData()将其传递到XML流读取器。然后调用自定义解析函数,从阅读器读取XML事件。
性能和内存消耗
QXmlStreamReader是内存保守的设计,因为它不存储整个XML文档树在内存中,但只有当前的令牌在报告时。 此外,QXmlStreamReader避免了许多小字符串分配,它通常需要将XML文档映射到一个方便的Qt-ish API。 它通过报告所有字符串数据作为QStringRef而不是真正的QString对象来做到这一点。 QStringRef是一个围绕QString子字符串的薄包装,它提供了QString API的一个子集,而没有内存分配和引用计数开销。 对任何这些对象调用toString()会返回一个等效的真正的QString对象。
使用具体流程是:
1. 创建一个QXmlStreamReader的类对象
2. 通过setDevice()设置好要处理的XML文件
3. 通过readNext()挨个读入节点
4.通过isStartElement()和isEndElement()判断是节点的开始和结束
5可以通过name()得到当前节点名字
6可以通过readElementText()访问当前节点的内容
7通过attributes()获取含有属性的节点的属性
实操:
#ifndef STREAMPARSEXML_2_H #define STREAMPARSEXML_2_H #include <QXmlStreamReader> class StreamParseXML_2 { public: StreamParseXML_2(); ~StreamParseXML_2(); void readXML(); void readXML_2(); private: QString m_sFileName; QXmlStreamReader *m_pReader; }; #endif // STREAMPARSEXML_2_H
/*读取流程 2、QXmlStreamReader接口说明 创建一个QXmlStreamReader的类对象 通过setDevice()设置好要处理的XML文件 通过readNext()挨个读入节点, 通过isStartElement()和isEndElement()判断是节点的开始和结束. 通过name()得到当前节点名字 通过readElementText()访问当前节点的内容 通过attributes()获取含有属性的节点的属性 */ /* <?xml version="1.0" encoding="UTF-8"?> <Msg> <Data id="1"> <title> <xmlname>hello.xml</xmlname> <owner>zhj</owner> </title> <Number>123456</Number> <Name>zhangjie</Name> <email>zhangjie@sina.cn</email> <website>zhangjie.hello.cn</website> </Data> <Data id="2"> <Number>789012</Number> <Name>haier</Name> <email>haier@sina.cn</email> <website>haier.sina.cn</website> </Data> <Data id="3"> <Number>345678</Number> <Name>sum</Name> <email>sum@sina.cn</email> <website>sum.sina.cn</website> </Data> <Data id="4"> <Number>901234</Number> <Name>niil</Name> <email>niil@sina.cn</email> <website>niil.sina.cn</website> </Data> </Msg> */ #include "StreamParseXML_2.h" #include <QApplication> #include <QFile> #include <QDebug> StreamParseXML_2::StreamParseXML_2() { m_sFileName = qApp->applicationDirPath() + "/" + "Test2.xml"; } StreamParseXML_2::~StreamParseXML_2() { } void StreamParseXML_2::readXML() { QFile file(m_sFileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug()<<"Open file hello.xml failure"; return ; } // 创建对象 QXmlStreamReader *pReader = new QXmlStreamReader(&file); while (!pReader->atEnd()) { // qDebug() << pReader->text() << pReader->readElementText()<<pReader->name(); // 判断节点是否是开始节点 if (pReader->isStartElement()) { /*此段代码存在逻辑漏洞,因为第一次进来的时候是MSG几点,然后获取所有的attribute,全部都是 * data节点,这样处理是不合适,如果msg下面有一堆的如 <Number>123456</Number> <Name>zhangjie</Name> 那么将会读取到这些数据,所以应该是拆分成子函数, 判断是否是data节点,if (pReader->name() == "Data"),然后才进入下面的详细的代码 修改参见readXML2() * * */ qDebug() << pReader->name(); // 将属性读出 例如id = 3 age = 23 QXmlStreamAttributes attributes = pReader->attributes(); // 判断是否存在属性id if (attributes.hasAttribute("id")) { qDebug() << "id:" << attributes.value("id").toString(); } // 判断当前节点的名字是否为number if (pReader->name() == "Number") { qDebug() << "Number:" << pReader->readElementText(); } //判断当前节点的名字是否为Name else if (pReader->name() == "Name") { qDebug() << "Name:" << pReader->readElementText(); } //判断当前节点的名字是否为email else if(pReader->name() == "email") { qDebug() << "email:" << pReader->readElementText(); } //判断当前节点的名字是否为website else if(pReader->name() == "website") { qDebug() << "website:" << pReader->readElementText(); } } //节点结束、并且节点名字为Data(含有子节点) else if(pReader->isEndElement() && pReader->name() == "Data") { qDebug() << "--------------------"; } pReader->readNext(); } file.close(); pReader->clear(); delete pReader; pReader = NULL; } void StreamParseXML_2::readXML_2() { QFile file(m_sFileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug()<<"Open file hello.xml failure"; return ; } // 创建对象 QXmlStreamReader *pReader = new QXmlStreamReader(&file); while (!pReader->atEnd()) { // 判断节点是否是开始节点 if (pReader->isStartElement()) { qDebug() << pReader->name(); if (pReader->name() == "Data") { // 将属性读出 例如id = 3 age = 23 QXmlStreamAttributes attributes = pReader->attributes(); // 判断是否存在属性id if (attributes.hasAttribute("id")) { qDebug() << "id:" << attributes.value("id").toString(); } // 判断当前节点的名字是否为number if (pReader->name() == "Number") { qDebug() << "Number:" << pReader->readElementText(); } //判断当前节点的名字是否为Name else if (pReader->name() == "Name") { qDebug() << "Name:" << pReader->readElementText(); } //判断当前节点的名字是否为email else if(pReader->name() == "email") { qDebug() << "email:" << pReader->readElementText(); } //判断当前节点的名字是否为website else if(pReader->name() == "website") { qDebug() << "website:" << pReader->readElementText(); } } } //节点结束、并且节点名字为Data(含有子节点) else if(pReader->isEndElement() && pReader->name() == "Data") { qDebug() << "--------------------"; } pReader->readNext(); } file.close(); pReader->clear(); delete pReader; pReader = NULL; }
因为QXmlStreamReader读取的时候使用pReader->readNext();读取下一行,所以读取的时候只会不断的读取下一行记录,不会判断是否退出,导致直接到xml最后,下面是判断是否结束的例子:
#ifndef STREAMPARSEXML_H #define STREAMPARSEXML_H #include <QXmlStreamReader> class StreamParseXML { public: StreamParseXML(); ~StreamParseXML(); void readXML(); void writeXML(); private: void parseUserInformation(); QString getValue(const QString &name); private: QString m_sFileName; QXmlStreamReader *m_pReader; }; #endif // STREAMPARSEXML_H
#include "StreamParseXML.h" #include <QFile> #include <QMessageBox> #include <QDebug> #include <QApplication> #include <QFileInfo> /* <?xml version="1.0" encoding="UTF-8"?> <COMMAND> <OBJECT>USER</OBJECT> <ACTION>LOGIN</ACTION> <DATA> <USER NAME="root" PASSWORD="123456"/> </DATA> <OBJECT>USER</OBJECT> <ACTION>LOGIN</ACTION> <DATA> <USER NAME="root1" PASSWORD="1234567"/> </DATA> </COMMAND> */ StreamParseXML::StreamParseXML() { qDebug() << qApp->applicationDirPath(); qDebug() << qApp->applicationDisplayName(); qDebug() << qApp->applicationName(); qDebug() << qApp->applicationFilePath(); m_sFileName = qApp->applicationDirPath() + "/" + "streamparse.xml"; } StreamParseXML::~StreamParseXML() { } void StreamParseXML::readXML() { if (m_sFileName.isEmpty()) return; QFile *pFile = new QFile(m_sFileName); if (!pFile->open(QIODevice::ReadOnly | QFile::Text)) { QMessageBox::information(NULL, QString("title"), QString("open error!")); return; } m_pReader = new QXmlStreamReader(pFile); while (!m_pReader->atEnd() && !m_pReader->hasError()) { m_pReader->lineNumber(); QXmlStreamReader::TokenType token = m_pReader->readNext(); if (token == QXmlStreamReader::StartDocument) continue; qDebug() << m_pReader->text(); if (m_pReader->isStartElement() && m_pReader->name() == "OBJECT") { QString elementText = m_pReader->readElementText(); if (elementText == "USER") { parseUserInformation(); } } } if (m_pReader->hasError()) { qDebug() << m_pReader->errorString(); } m_pReader->clear(); delete m_pReader; m_pReader = NULL; pFile->close(); delete pFile; pFile = NULL; } void StreamParseXML::writeXML() { QFileInfo fileInfo(m_sFileName); QString sFileName = fileInfo.absolutePath() + "/" + "Write.xml"; QFile file(sFileName); 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.writeComment("AAAA"); // 添加注释 writer.writeCDATA("DDDDD"); // 添加CDDATA writer.writeCharacters("zhongjianshuju"); // writer.writeStartElement("Text"); writer.writeTextElement("Text", "123456789");// 添加一行 // writer.writeEndElement(); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndElement(); file.close(); } return ; } void StreamParseXML::parseUserInformation() { QString elementString = getValue("ACTION"); if (elementString == "LOGIN") { while(!m_pReader->atEnd()) { m_pReader->readNext(); qDebug() << "**********"; qDebug() << m_pReader->lineNumber(); qDebug() << m_pReader->name(); qDebug() << m_pReader->isEndElement(); // 用于判断当前标签结束了,那就不继续走了,否则会在这里一直到结束 if ("DATA" == m_pReader->name() && m_pReader->isEndElement()) break; if (m_pReader->name() == "USER") { QXmlStreamAttributes attributes = m_pReader->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(!m_pReader->atEnd()) { m_pReader->readNext(); if (m_pReader->isStartElement() && m_pReader->name() == name) { return m_pReader->readElementText(); } } return ""; }
posted on 2017-04-05 09:27 965452300 阅读(2320) 评论(0) 编辑 收藏 举报