天天看點

XML檔案格式詳解及Qt環境下解析

文章目錄

      • XML檔案簡介
      • XML檔案格式
        • 基本格式
        • 注意事項
      • Qt環境下XML解析示例
        • 示例1解析
        • 示例2解析

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必須有而且隻能有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>
           
XML檔案格式詳解及Qt環境下解析

示例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檔案格式詳解及Qt環境下解析

注意事項

  • 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檔案格式詳解及Qt環境下解析

文本内容:

<?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檔案格式詳解及Qt環境下解析

文本檔案内容:

<?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"