在下面的随笔中,我会根据xml的结构,给出Qt中解析这个xml的三种方式的代码。虽然,这个代码时通过调用Qt的函数实现的,但是,很多开源的C++解析xml的库,甚至很多其他语言解析xml的库,都和下面三种解析xml采用相同的原理,所以就算你不是学习qt,也可以大致参看一下代码,对三种解析方式有一种大致的感觉。

先给出xml如下:

<?xml version="1.0" encoding="utf-8"?>
<school>
<teacher>
<entry name="Job">
<age>30</age>
<sport>soccer</sport>
</entry>
<entry name="Tom">
<age>32</age>
<sport>swimming</sport>
</entry>
</teacher>
<student>
<entry name="Lily">
<age>20</age>
<sport>dancing</sport>
</entry>
<entry name="Keith">
<age>21</age>
<sport>running</sport>
</entry>
</student>
</school>

下面给出qt中解析xml的三种方式,通过解析xml,创建student列表和teacher列表。先给出存储的结构体和辅助函数:

#include <string>
#include <ostream> namespace School
{ struct Teacher
{
std::string name;
int age;
std::string loveSport; Teacher(std::string name_, int age_, std::string loveSport_)
: name(std::move(name_)), age(age_), loveSport(std::move(loveSport_))
{ }
}; struct Student
{
std::string name;
int age;
std::string loveSport; Student(std::string name_, int age_, std::string loveSport_)
: name(std::move(name_)), age(age_), loveSport(std::move(loveSport_))
{ }
}; inline void print(std::ostream &out, const Teacher& teacher)
{
out << "teacher: " << teacher.name << std::endl;
out << "\tage: " << teacher.age << std::endl;
out << "\tfavorite sport: " << teacher.loveSport << std::endl;
} inline void print(std::ostream& out, const Student& student)
{
out << "student: " << student.name << std::endl;
out << "\tage: " << student.age << std::endl;
out << "\tfavorite sport: " << student.loveSport << std::endl;
} }

另外需要注意在.pro中添加

QT += xml

(1)通过QXmlStreamReader:

#include <QXmlStreamReader>
#include "schooldefine.h" class XmlStreamReader
{
public:
XmlStreamReader(); bool readFile(const QString& fileName);
void printAllMembers(); private:
void readSchoolMembers();
void readTeacherMembers();
void readTeacher(const QStringRef& teacherName);
void readStudentMembers();
void readStudent(const QStringRef& studentName);
void skipUnknownElement(); QXmlStreamReader reader; std::vector<School::Teacher> m_teachers;
std::vector<School::Student> m_students;
};
#include "XmlStreamReader.h"
#include <QFile>
#include <iostream>
#include <QDebug> XmlStreamReader::XmlStreamReader()
{ } bool XmlStreamReader::readFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
std::cerr << "Error: Cannot read file " << qPrintable(fileName)
<< ": " << qPrintable(file.errorString())
<< std::endl;
return false;
}
reader.setDevice(&file); reader.readNext();
while (!reader.atEnd())
{
if (reader.isStartElement())
{
if (reader.name() == "school")
{
readSchoolMembers();
}
else
{
reader.raiseError(QObject::tr("Not a school file"));
}
}
else
{
reader.readNext();
}
} file.close();
if (reader.hasError())
{
std::cerr << "Error: Failed to parse file "
<< qPrintable(fileName) << ": "
<< qPrintable(reader.errorString()) << std::endl;
return false;
}
else if (file.error() != QFile::NoError)
{
std::cerr << "Error: Cannot read file " << qPrintable(fileName)
<< ": " << qPrintable(file.errorString())
<< std::endl;
return false;
}
return true;
} void XmlStreamReader::printAllMembers()
{
std::cout << "All teachers: " << std::endl;
for (const auto& teacher : m_teachers)
{
School::print(std::cout, teacher);
}
std::cout << "All students: " << std::endl;
for (const auto& student : m_students)
{
School::print(std::cout, student);
}
} void XmlStreamReader::readSchoolMembers()
{
reader.readNext();
while (!reader.atEnd())
{
if (reader.isEndElement())
{
reader.readNext();
break;
} if (reader.isStartElement())
{
if (reader.name() == "teacher")
{
readTeacherMembers();
}
else if (reader.name() == "student")
{
readStudentMembers();
}
else
{
skipUnknownElement();
}
}
else
{
reader.readNext();
}
}
} void XmlStreamReader::readTeacherMembers()
{
reader.readNext();
while (!reader.atEnd())
{
if (reader.isEndElement())
{
reader.readNext();
break;
} if (reader.isStartElement())
{
if (reader.name() == "entry")
{
readTeacher(reader.attributes().value("name"));
}
else
{
skipUnknownElement();
}
}
else
{
reader.readNext();
}
}
} void XmlStreamReader::readTeacher(const QStringRef& teacherName)
{
reader.readNext(); int age = ;
std::string favoriteSport; while (!reader.atEnd())
{
if (reader.isEndElement())
{
reader.readNext();
break;
} if (reader.isStartElement())
{
if (reader.name() == "age")
{
age = reader.readElementText().toInt();
}
else if (reader.name() == "sport")
{
favoriteSport = reader.readElementText().toStdString();
}
else
{
skipUnknownElement();
}
}
reader.readNext();
} m_teachers.emplace_back(teacherName.toString().toStdString(), age, favoriteSport);
} void XmlStreamReader::readStudentMembers()
{
reader.readNext();
while (!reader.atEnd())
{
if (reader.isEndElement())
{
reader.readNext();
break;
} if (reader.isStartElement())
{
if (reader.name() == "entry")
{
readStudent(reader.attributes().value("name"));
}
else
{
skipUnknownElement();
}
}
else
{
reader.readNext();
}
}
} void XmlStreamReader::readStudent(const QStringRef &studentName)
{
reader.readNext(); int age = ;
std::string favoriteSport; while (!reader.atEnd())
{
if (reader.isEndElement())
{
reader.readNext();
break;
} if (reader.isStartElement())
{
if (reader.name() == "age")
{
age = reader.readElementText().toInt();
}
else if (reader.name() == "sport")
{
favoriteSport = reader.readElementText().toStdString();
}
else
{
skipUnknownElement();
}
}
reader.readNext();
} m_students.emplace_back(studentName.toString().toStdString(), age, favoriteSport);
} void XmlStreamReader::skipUnknownElement()
{
reader.readNext();
while (!reader.atEnd())
{
if (reader.isEndElement())
{
reader.readNext();
break;
} if (reader.isStartElement())
{
skipUnknownElement();
}
else
{
reader.readNext();
}
}
}

(2)通过DOM方式:

#include <QString>
#include <QDomElement>
#include "schooldefine.h" class DomParser
{
public:
DomParser(); bool readFile(const QString &fileName);
void printAllMembers(); private:
void parseSchoolMembers(const QDomElement &element);
void parseTeacherMembers(const QDomElement &element);
void parseStudentMembers(const QDomElement &element);
void parseTeacher(const QDomElement &element);
void parseStudent(const QDomElement &element); std::vector<School::Teacher> m_teachers;
std::vector<School::Student> m_students;
};
#include "domparser.h"
#include <QDomDocument>
#include <QFile>
#include <iostream> DomParser::DomParser()
{ } bool DomParser::readFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
std::cerr << "Error: Cannot read file " << qPrintable(fileName)
<< ": " << qPrintable(file.errorString())
<< std::endl;
return false;
} QString errorStr;
int errorLine;
int errorColumn; QDomDocument doc;
if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn))
{
std::cerr << "Error: Parse error at line " << errorLine << ", "
<< "column " << errorColumn << ": "
<< qPrintable(errorStr) << std::endl;
return false;
} QDomElement root = doc.documentElement();
if (root.tagName() != "school")
{
std::cerr << "Error: Not a school file" << std::endl;
return false;
} parseSchoolMembers(root);
return true;
} void DomParser::printAllMembers()
{
std::cout << "All teachers: " << std::endl;
for (const auto& teacher : m_teachers)
{
School::print(std::cout, teacher);
}
std::cout << "All students: " << std::endl;
for (const auto& student : m_students)
{
School::print(std::cout, student);
}
} void DomParser::parseSchoolMembers(const QDomElement &element)
{
QDomNode child = element.firstChild();
while (!child.isNull())
{
if (child.toElement().tagName() == "teacher")
{
parseTeacherMembers(child.toElement());
}
else if (child.toElement().tagName() == "student")
{
parseStudentMembers(child.toElement());
}
child = child.nextSibling();
}
} void DomParser::parseTeacherMembers(const QDomElement &element)
{
QDomNode child = element.firstChild();
while (!child.isNull())
{
if (child.toElement().tagName() == "entry")
{
parseTeacher(child.toElement());
}
child = child.nextSibling();
}
} void DomParser::parseStudentMembers(const QDomElement &element)
{
QDomNode child = element.firstChild();
while (!child.isNull())
{
if (child.toElement().tagName() == "entry")
{
parseStudent(child.toElement());
}
child = child.nextSibling();
}
} void DomParser::parseTeacher(const QDomElement &element)
{
auto children = element.childNodes();
auto firstChild = children.at().toElement();
auto secondChild = children.at().toElement();
int age = firstChild.text().toInt(); m_teachers.emplace_back(element.attribute("name").toStdString(),
age, secondChild.text().toStdString());
} void DomParser::parseStudent(const QDomElement &element)
{
auto children = element.childNodes();
auto firstChild = children.at().toElement();
auto secondChild = children.at().toElement();
int age = firstChild.text().toInt(); m_students.emplace_back(element.attribute("name").toStdString(),
age, secondChild.text().toStdString());
}

3. 采用QXmlSimpleReader方式,也就是回调函数方式:

#include <QXmlDefaultHandler>
#include "schooldefine.h" class SaxHandler : public QXmlDefaultHandler
{
public:
SaxHandler(); bool readFile(const QString &fileName);
void printAllMembers(); protected:
bool startElement(const QString &namespaceURI,
const QString &localName,
const QString &qName,
const QXmlAttributes &atts) override;
bool endElement(const QString &namespaceURL,
const QString &localName,
const QString &qName) override;
bool characters(const QString &ch) override;
bool fatalError(const QXmlParseException &exception) override; private:
bool m_isStudent = false;
QString m_currentContext;
std::vector<School::Teacher> m_teachers;
std::vector<School::Student> m_students;
};
#include "saxhandler.h"
#include <iostream> SaxHandler::SaxHandler()
{ } bool SaxHandler::readFile(const QString &fileName)
{
QFile file(fileName);
QXmlInputSource inputSource(&file);
QXmlSimpleReader reader;
reader.setContentHandler(this);
reader.setErrorHandler(this);;
return reader.parse(inputSource);
} void SaxHandler::printAllMembers()
{
std::cout << "All teachers: " << std::endl;
for (const auto& teacher : m_teachers)
{
School::print(std::cout, teacher);
}
std::cout << "All students: " << std::endl;
for (const auto& student : m_students)
{
School::print(std::cout, student);
}
} bool SaxHandler::startElement(const QString &namespaceURI,
const QString &localName,
const QString &qName,
const QXmlAttributes &atts)
{
if (qName == "teacher")
{
m_isStudent = false;
}
else if (qName == "student")
{
m_isStudent = true;
}
else if (qName == "entry")
{
if (m_isStudent)
{
m_students.push_back(School::Student("", , ""));
m_students.back().name = atts.value("name").toStdString();
}
else
{
m_teachers.push_back(School::Teacher("", , ""));
m_teachers.back().name = atts.value("name").toStdString();
}
}
else if (qName == "age")
{
m_currentContext.clear();
}
else if (qName == "sport")
{
m_currentContext.clear();
}
return true;
} bool SaxHandler::characters(const QString &ch)
{
m_currentContext += ch;
return true;
} bool SaxHandler::endElement(const QString &namespaceURL,
const QString &localName,
const QString &qName)
{
if (qName == "age")
{
if (m_isStudent)
{
m_students.back().age = m_currentContext.toInt();
}
else
{
m_teachers.back().age = m_currentContext.toInt();
}
}
else if (qName == "sport")
{
if (m_isStudent)
{
m_students.back().loveSport = m_currentContext.toStdString();
}
else
{
m_teachers.back().loveSport = m_currentContext.toStdString();
}
}
m_currentContext.clear();
return true;
} bool SaxHandler::fatalError(const QXmlParseException &exception)
{
std::cerr << "Parse error at line" << exception.lineNumber()
<< ", " << "column " << exception.columnNumber() << ": "
<< qPrintable(exception.message()) << std::endl;
return false;
}

下面简单对上述三种方式予以说明:

(1) 从代码行数来看,采用DOM和QXmlSimpleReader的方式,代码行数比较少,而QXmlStreamReader代码行数较多。

(2) 从代码逻辑分析来看,采用DOM方式最容易理解,采用QXmlStreamReader的方式稍微难理解一些,而采用QXmlSimpleReader由于使用了较多的回调,引入了大量的类数据成员,使得代码会很难理解。

(3) 从内存占用来看,DOM的方式会耗费最多的内存,因为需要一次性将所有的内容构建成树,DOM和QXmlSimpleReader对内存要求都较低。

(4) 从运行时间消耗来看,DOM的消耗,可能会稍微大一些,因为DOM正常要经历2次的遍历,一次遍历构建树,一次遍历,构建自己需要的数据。而QXmlSimpleReader和QXmlStreamReader正常只需要遍历一次。

(5) 从处理异常来看,DOM和QXmlStreamReader应该会更容易一些,因为不涉及回调函数,但是对于xml来说,很多时候主要确认内容正确与否,如果错误就退出,查看xml中的错误。当然,这个也是比较重要的项。

对于我来说,因为大多数情况下,解析的xml不是很大,而且基本只涉及加载过程中,所以使用DOM的情况比较多。如果xml比较大,或者调用比较频繁,可以考虑使用QXmlStreamReader的方式。

Qt中三种解析xml的方式的更多相关文章

  1. windows phone中三种解析XML的方法

    需求如下, 项目需要将一段xml字符串中的信息提取出来 <?xml version=""1.0"" encoding=""UTF-8& ...

  2. Spring中三种配置Bean的方式

    Spring中三种配置Bean的方式分别是: 基于XML的配置方式 基于注解的配置方式 基于Java类的配置方式 一.基于XML的配置 这个很简单,所以如何使用就略掉. 二.基于注解的配置 Sprin ...

  3. 【转】ZYNQ中三种实现GPIO的方式

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/husipeng86/article/det ...

  4. Android中三种常用解析XML的方式(DOM、SAX、PULL)简介及区别

    XML在各种开发中都广泛应用,Android也不例外.作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能.今天就由我向大家介绍一下在Android平台下几种常见的XML解 ...

  5. Qt中使用DOM解析XML文件或者字符串二(实例)

    介绍 在Qt中提供了QtXml模块实现了对XML数据的处理,我们在Qt帮助中输入关键字QtXml Module,可以看到该模块的类表.在这里我们可以看到所有相关的类,它们主要是服务于两种操作XML文档 ...

  6. smarty中三种变量的访问方式

    在模板中smarty有三种变量,第一种,php分配的变量,第二种配置文件里的变量,第三种,PHP全局数组里的变量,配置文件里变量的访问方式可以是{#bgcolor#},"#"必须紧 ...

  7. Selenium中三种等待的使用方式---规避网络延迟、代码不稳定问题

    在UI自动化测试中,必然会遇到环境不稳定,网络慢的情况,这时如果你不做任何处理的话,代码会由于没有找到元素,而报错.这时我们就要用到wait(等待),而在Selenium中,我们可以用到一共三种等待, ...

  8. Android Studio项目中三种依赖的添加方式

    通常一个AS项目中的依赖关系有三种,一是本地依赖(主要是对本地的jar包),二是模块依赖,三是远程依赖:添加这些依赖的目的在于上我们想要在项目的某一个模块中使用其中的功能,比如okttp这个网络框架库 ...

  9. Qt中使用DOM解析XML文件或者字符串(实例)

    因为需要读取配置文件,我的配置文件采用xml:因此编写了使用qt读取xml文件内容的代码,xml文件如下: <?xml version="1.0" encoding=&quo ...

随机推荐

  1. Hyperledger fabric-sdk-java Basics Tutorial(转)

    原文地址:Hyperledger fabric-sdk-java Basics Tutorial This quick tutorial is for all Java developers, who ...

  2. zzw原创_非root用户启动apache的问题解决(非root用户启动apache的1024以下端口)

    场景:普通用户编译的apache,要在该用户下启动1024端口以下的apache端口 1.假设普通用户为sims20,用该用户编译 安装了一个apache,安装路径为/opt/aspire/produ ...

  3. Linux中利用extundelete恢复误删除的数据

    利用extundelete工具恢复磁盘误删除的数据 原理: 简单介绍下关于inode的知识.在Linux下可以通过"ls -id"命令来查看某个文件或者目录的inode值,例如查看 ...

  4. PC/FORTH 变量|常数|数组

    body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...

  5. format格式化和函数

    {[name][:][[fill]align][sign][#][0][width][,][.precision][type]}用{ }包裹name命名传递给format以命名=值 写法, 非字典映射 ...

  6. 剑指Offer 5. 用两个栈实现队列 (栈)

    题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 题目地址 https://www.nowcoder.com/practice/54275ddae22f4 ...

  7. VS Code + NWJS(Node-Webkit)0.14.7 + SQLite3 + Angular6 构建跨平台桌面应用

    一.项目需求 最近公司有终端桌面系统需求,需要支持本地离线运行(本地数据为主,云端数据同步),同时支持Window XP,最好跨平台.要求安装配置简单(一次性打包安装),安装包要小,安装时间短,可离线 ...

  8. iOS动画学习

    学习一下动画,感谢以下大神的文章:    UIView:基础动画.关键帧动画.转场动画 Core Animation :基础动画,关键帧动画,动画组,转场动画,逐帧动画 CALayer :CALaye ...

  9. SLEUTH 城市扩张模型

    3.19号准备试着运行一下SLEUTH模型,但是好不容易没报错出了一个test的结果,我就再也没看过了,导致现在我竟然差不多忘记当时怎么搞出来的了... 这也提醒我了,,,以后解决一个什么东西一定要立 ...

  10. Reactor

    Flux和Mono Flux和Mono是Reactor中的两个基本概念.Flux表示的是包含0到N个元素的异步序列.在该序列中可以包含三种不同类型的消息通知:正常的包含元素的消息,序列结束的消息和序列 ...