XML文件格式详解及Qt环境下解析

1,712 阅读4分钟

@[toc]

XML文件简介

xml,一般指可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。XML有两个先驱:SGML和HTML,这两个语言都是非常成功的标记语言,但是都有一些与生俱来的缺陷。XML正是为了解决它们的不足而诞生的。

简单的说,就是按照一定的格式,把数据表示出来。和JSON格式很像,关于JSON数据格式及其解析可以参考以下文章:

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"