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()获取含有属性的节点的属性
*/
error parse new/*
<?xml version="1.0" encoding="UTF-8"?>
<Msg>
<Data id="1">
<title>
<xmlname&l</xmlname>
<owner>zhj</owner>
</title>
<Number>123456</Number>
<Name>zhangjie</Name>
<email>zhangjie@sina</email>
<website>zhangjie.hello</website>
</Data>
<Data id="2">
<Number>789012</Number>
<Name>haier</Name>
<email>haier@sina</email>
<website>haier.sina</website>
</Data>
<Data id="3">
<Number>345678</Number>
<Name>sum</Name>
<email>sum@sina</email>
<website>sum.sina</website>
</Data>
<Data id="4">
<Number>901234</Number>
<Name>niil</Name>
<email>niil@sina</email>
<website>niil.sina</website>
</Data>
</Msg>
*/
#include "StreamParseXML_2.h"
#include <QApplication>
#include <QFile>
#include <QDebug>
StreamParseXML_2::StreamParseXML_2()
{
m_sFileName = qApp->applicationDirPath() + "/" + "l";
}
StreamParseXML_2::~StreamParseXML_2()
{
}
void StreamParseXML_2::readXML()
{
QFile file(m_sFileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug()<<"Open l 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 l 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();

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。