@[toc]
XML文件简介
xml,一般指可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。XML有两个先驱:SGML和HTML,这两个语言都是非常成功的标记语言,但是都有一些与生俱来的缺陷。XML正是为了解决它们的不足而诞生的。
简单的说,就是按照一定的格式,把数据表示出来。和JSON格式很像,关于JSON数据格式及其解析可以参考以下文章:
- JSON格式简介
- 使用cJSON库解析JSON
- 使用cJSON库构建JSON字符串
- Qt平台下使用QJson解析和构建JSON字符串
- Keil环境下Jansson解析库的使用——基于STM32F103
XML文件格式
XML是树形结构,通常由根节点+子节点组成,或者叫根元素+子元素组成。
基本格式
XML文件第一行必须是声明语句:
<?xml version="1.0" encoding="UTF-8"?>
声明语句之后,是根元素,XML必须有而且只能有1个根元素,名称任意:
<?xml version="1.0" encoding="UTF-8"?>
<root>
</root>
XML语句格式:元素+属性
<元素名 属性名=“属性值”>
</元素名>
<元素名 属性名1=“属性值1” 属性名2=“属性值2”>
</元素名>
//当元素不包含子元素时,可以简写为以下格式
<元素名 属性名=“属性值”/>
<元素名 属性名1=“属性值1” 属性名2=“属性值2”/>
示例:
//元素不包含子元素
<user blog="www.wangchaochao.top" wechat="mcu149"/>
<user csdn_id="whik1194" wechat="mcu149"/>
也可以写成
<user blog="www.wangchaochao.top" wechat="mcu149">
</user>
<user csdn_id="whik1194" wechat="mcu149">
</user>
元素中包含子元素
<user csdn_id="whik1194">
<home>https://blog.csdn.net/whik1194</home>
<wechat>mcu149</wechat>
<blog>www.wangchaochao.top</blog>
</user>
示例1,全部采用元素的方式:
<?xml version="1.0" encoding="utf-8"?>
<root>
<csdn id="whik1194">
<home>https://blog.csdn.net/whik1194</home>
<follower>709</follower>
</csdn>
<zhihu id="wangchao149">
<home>https://www.zhihu.com/people/wangchao149</home>
<follower>4961</follower>
</zhihu>
</root>
示例2,采用元素+属性的方式:
<?xml version="1.0" encoding="utf-8"?>
<root>
<csdn id="whik1194" home="https://blog.csdn.net/whik1194" follower="709"/>
<zhihu id="wangchao149" home="https://www.zhihu.com/people/wangchao149" follower="4961"></zhihu>
</root>
注意事项
- XML区分大小写,而且第一个字符不能是数字或下划线
- 标记必须成对出现,有一个开始标记,就必须有一个结束标记,否则认为语法错误
- XML规定,所有属性值必须加引号,可以是单引号,或双引号,建议统一使用双引号
Qt环境下XML解析示例
Qt环境下解析XML主要有以下3种方式:
- QXmlStreamReader
- DOM(Document Object Model)
- SAX(Simple API for XML)
优缺点对比参考:
- 从代码行数来看,采用DOM和QXmlSimpleReader的方式,代码行数比较少,而QXmlStreamReader代码行数较多。
- 从代码逻辑分析来看,采用DOM方式最容易理解,采用QXmlStreamReader的方式稍微难理解一些,而采用QXmlSimpleReader由于使用了较多的回调,引入了大量的类数据成员,使得代码会很难理解。
- 从内存占用来看,DOM的方式会耗费最多的内存,因为需要一次性将所有的内容构建成树,DOM和QXmlSimpleReader对内存要求都较低。
- 从运行时间消耗来看,DOM的消耗,可能会稍微大一些,因为DOM正常要经历2次的遍历,一次遍历构建树,一次遍历,构建自己需要的数据。而QXmlSimpleReader和QXmlStreamReader正常只需要遍历一次。
- 从处理异常来看,DOM和QXmlStreamReader应该会更容易一些,因为不涉及回调函数,但是对于xml来说,很多时候主要确认内容正确与否,如果错误就退出,查看xml中的错误。当然,这个也是比较重要的项。
对于我来说,因为大多数情况下,解析的xml不是很大,而且基本只涉及加载过程中,所以使用DOM的情况比较多。如果xml比较大,或者调用比较频繁,可以考虑使用QXmlStreamReader的方式。
至于生成 XML 文档,Qt 同样提供了三种方式:
- QXmlStreamWriter,与QXmlStreamReader相对应;
- DOM 方式,首先在内存中生成 DOM 树,然后将 DOM 树写入文件。不过,除非我们程序的数据结构中本来就维护着一个 DOM 树,否则,临时生成树再写入肯定比较麻烦;
- 纯手工生成 XML 文档,显然,这是最复杂的一种方式。
本文只讲解基于DOM如何解析XML。
首先,Qt需要添加XML支持,在.pro文件中添加一行:
QT += xml
并将XML文件添加到资源路径,或者不添加直接通过文件名访问。
头文件包含:
#include <QDomDocument>
#include <QDomElement>
基本操作,文件的打开:
bool MainWindow::parseXML(QString fileName)
{
//step1.XML文件打开
if(fileName.isEmpty())
{
qDebug() << "fileName is empty";
return false;
}
QFile file(fileName);
if(!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::warning(this, "Warning", fileName + "\n" + file.errorString());
return false;
}
qDebug() << "file open success: " << fileName;
// .........
}
然后加载到DOM中,并验证XML语法格式,如果语法格式不正确,错误信息保存在errStr中。
//step2.XML文件加载到DOM中,并验证语法格式
QDomDocument docXML;
QString errStr;
int row, column;
if(!docXML.setContent(&file, false, &errStr, &row, &column))
{
QMessageBox::warning(this, "Warning", \
fileName + "\nsetContent failed at line: " + QString::number(row, 10) + "\n" + errStr);
file.close();
return false;
}
qDebug() << "docXML setContent success";
file.close();
以上两个步骤是所有XML文件解析都必须进行的操作。下面来根据不同的XML文件格式来进行解析。
示例1解析
XML文件内容
文本内容:
<?xml version="1.0" encoding="utf-8"?>
<root>
<csdn id="whik1194">
<home>https://blog.csdn.net/whik1194</home>
<follower>709</follower>
</csdn>
<zhihu id="wangchao149">
<home>https://www.zhihu.com/people/wangchao149</home>
<follower>4961</follower>
</zhihu>
</root>
解析函数:
bool MainWindow::parseXML(QString fileName)
{
//step1.XML文件打开
//step2.XML文件加载到DOM中,并验证语法格式
//step3.XML解析
QDomElement xmlRoot = docXML.documentElement();
//获取根节点名称
if (xmlRoot.tagName() != "root")
{
qDebug() << "root tagName is not match";
return false;
}
//子节点列表
QDomNodeList nodeList = xmlRoot.childNodes();
qDebug() << "nodeList size: " << nodeList.size();
//csdn
for(int i = 0; i < nodeList.size(); i++)
{
QDomNode node = nodeList.at(i);
QDomElement element = node.toElement();
QString tagName = element.tagName(); //"csdn" or "zhihu"
QString id = element.attribute("id");
qDebug() << tagName << id;
//获取所有的子节点: home & follower
QDomNodeList eleNodeList= element.childNodes();
for(int j = 0; j < eleNodeList.size(); j++)
{
QDomElement ele = eleNodeList.at(j).toElement();
QString eleTagName = ele.tagName(); //"home" or "follower"
//"https://blog.csdn.net/whik1194" or "709"
//"https://www.zhihu.com/people/wangchao149" or "4961"
QString eleTagValue = ele.text();
qDebug() << eleTagName << eleTagValue;
}
qDebug() << "-----------";
}
return true;
}
运行结果:
file open success: ":/xml/demo.xml"
docXML setContent success
nodeList size: 2
"csdn" "whik1194"
"home" "https://blog.csdn.net/whik1194"
"follower" "709"
-----------
"zhihu" "wangchao149"
"home" "https://www.zhihu.com/people/wangchao149"
"follower" "4961"
-----------
示例2解析
XML文件内容:
文本文件内容:
<?xml version="1.0" encoding="utf-8"?>
<root>
<csdn id="whik1194" home="https://blog.csdn.net/whik1194" follower="709"/>
<zhihu id="wangchao149" home="https://www.zhihu.com/people/wangchao149" follower="4961"></zhihu>
</root>
解析函数:
bool MainWindow::parseXML(QString fileName)
{
//step1.XML文件打开
//step2.XML文件加载到DOM中,并验证语法格式
//step3.XML解析
QDomElement xmlRoot = docXML.documentElement();
//获取根节点名称
if (xmlRoot.tagName() != "root")
{
qDebug() << "root tagName is not match";
return false;
}
//子节点列表
QDomNodeList nodeList = xmlRoot.childNodes();
qDebug() << "nodeList size: " << nodeList.size();
//csdn
for(int i = 0; i < nodeList.size(); i++)
{
QDomNode node = nodeList.at(i);
QDomElement element = node.toElement();
QString tagName = element.tagName(); //"csdn" or "zhihu"
QString id = element.attribute("id");
QString home = element.attribute("home");
QString follower = element.attribute("follower");
qDebug() << tagName << id << home << follower;
qDebug() << "-----------";
}
return true;
}
运行结果:
file open success: ":/xml/demo.xml"
docXML setContent success
nodeList size: 2
"csdn" "whik1194" "https://blog.csdn.net/whik1194" "709"
"zhihu" "wangchao149" "https://www.zhihu.com/people/wangchao149" "4961"