1. 程式人生 > >Qt---Xml文件解析

Qt---Xml文件解析

區別 xmlreader -s item sco yellow styles placement 發的

本文我們通過一個讀取Xml文件的小例子來學習QXmlStreamReader。

Xml 簡介

Xml的全稱是可擴展標記語言(EXtensible Markup Language),同HTML一樣是一種標記語言。但是與HTML不同,XML:

  • 被設計為具有自我描述性
  • 沒有預定義標簽,需要使用者自行定義。
  • 設計宗旨是傳輸數據,而非顯示數據(HTML)。大多數Android App開發都用Xml文件作為界面開發的數據載體。

QXmlStreamReader 簡介

QXmlStreamReader is a faster and more convenient replacement for Qt‘s own SAX parser (see QXmlSimpleReader).

官方宣稱這是一個比Qt的SAX(Simple API for XML)解析器更快、更方便的替代,也就是說建議你優先使用它來解析Xml文件。

QXmlStreamReader和SAX的工作原理類似,都是以Token為單位對Xml文件進行讀取解析。使用QXmlStreamReader基本上有兩種模式:

技術分享圖片

這兩種方法的區別就是處理單位的不同,分別以Token、Element為單位:前者粒度更細,把控可以更精準,但相對的需要花更多的功夫在細節處理上;後者只關註元素,對於簡單的處理使用起來很方便。在QXmlStreamReader中,讀取Token需要調用QXmlStreamReader::readNext()

函數,該函數返回讀取到的Token,不同的Token類型需要調用不同的函數來獲取相關的內容,詳細內容參見Qt官方鏈接;而讀取元素則是調用QXmlStreamReader::readNextStartElement()函數,註意此函數簡單地區別頭元素和尾元素,如果是頭元素則返回true,否則(尾元素或出錯)返回false。

解析Xml 元素

Xml中的元素與HTML一樣:

<title>Colombia Earthquake</title>

這就是一個title元素,包含一些文本內容。我們通過調用QXmlStreamReader::readNextStartElement()

來讀取它,當讀取的是一個元素的時候,QXmlStreamReader::name()函數返回元素的名字,QXmlStreamReader::readElementText()返回元素內的文本。

解析Xml 文檔元素

<?xml version="1.0" encoding="ISO-8859-1"?>

Xml文檔元素包含常見的xml版本、編碼、獨立文檔(standalone)等元數據,這些屬性分別對應QXmlStreamReader的documentVersion()documentEncoding()isStandaloneDocument()等函數,但是在調用這些函數前我們需要先調用readNext()函數讓解析器先去讀取這個特殊元素。

void MainWindow::readDocumentElement()
{
    m_xmlReader.readNext();
    if (m_xmlReader.isStartDocument()) {
        auto item = new QTreeWidgetItem(
                    QStringList("Document Element"));
        item->setText(1, "xml version:" + m_xmlReader.documentVersion().toString() +
                    "    encoding:" + m_xmlReader.documentEncoding().toString() +
                    "    is standalone:" + (m_xmlReader.isStandaloneDocument() ? QString("true") : QString("false")));
        item->setBackgroundColor(1, QColor(Qt::green));
        m_treeWidget.addTopLevelItem(item);
    }
}

解析Xml CDATA

CDATA(Character Data) 代表字符數據,這個區段中的文本不會被Xml 解析器解析,而是原樣保留輸出。CDATA區段以<![CDATA[開始,以]]>結束,這兩個標記符號中間的文本可以是]]>以外的任何字符。CDATA經常被用來存儲那些包含特殊字符(Xml關鍵字或者保留字符)的文本,如下:

<![CDATA[ This is a <CDATA> section text! ]]>

<在Xml是特殊字符,用來標識元素的開始,如果上面這段文字不寫在CDATA區段中,

void MainWindow::readCDATA()
{
    while (!m_xmlReader.isCDATA()) {
        m_xmlReader.readNext();
    }
    auto item = new QTreeWidgetItem(QStringList("[CDATA]"));
    item->setText(1, m_xmlReader.text().toString());
    item->setBackgroundColor(1, QColor(Qt::cyan));
    m_treeWidget.addTopLevelItem(item);
}

因為CDATA區段不是元素,因此我們需要調用QXmlStreamReader::readNext()函數。

解析Xml DTD

文檔類型定義(DTD)可定義合法的XML文檔構建模塊。它使用一系列合法的元素來定義文檔的結構。

簡而言之,DTD是用來描述Xml文檔的結構的,語法如下:

<!DOCTYPE 根元素 [元素聲明]>

例如下面這個DTD:

<!DOCTYPE note [
    <!ELEMENT note (to,from,heading,body)>
    <!ELEMENT to      (#PCDATA)>
    <!ELEMENT from    (#PCDATA)>
    <!ELEMENT heading (#PCDATA)>
    <!ELEMENT body    (#PCDATA)>
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don‘t forget the meeting!</body>
</note>

該DTD 解釋如下:

!DOCTYPE note (第1行)定義此文檔是 note 類型的文檔。
!ELEMENT note (第2行)定義 note 元素有四個元素:"to、from、heading,、body"
!ELEMENT to (第3行)定義 to 元素為 "#PCDATA" 類型
!ELEMENT from (第4行)定義 from 元素為 "#PCDATA" 類型
!ELEMENT heading (第5行)定義 heading 元素為 "#PCDATA" 類型
!ELEMENT body (第6行)定義 body 元素為 "#PCDATA" 類型

用QXmlStreamReader解析DTD也非常方便,方法與解析CDATA一樣,只是換了個判斷函數:

while (!m_xmlReader.isDTD()) {
    m_xmlReader.readNext();
}
auto item = new QTreeWidgetItem(QStringList("[DTD]"));
item->setText(1, m_xmlReader.text().toString());
item->setBackgroundColor(1, QColor(Qt::darkMagenta));
m_treeWidget.addTopLevelItem(item);

解析Xml 註釋

Xml 的註釋也與HTML相同,<!-- XXXXXXXXXXXXXXXXXXX -->,代碼與上一小節基本一樣,只是判斷處改用了QXmlStreamReader::isComment()

while (!m_xmlReader.isComment()) {
    m_xmlReader.readNext();
}
...

解析Xml Processing Instruction

XMl PI(Processing Instruction) 處理指令使用這種格式<?PITarget PIContent?>,前一部分是Target,後一部分是Content。Processing Instruction用來告訴Xml應用程序進行一些Xml以外的操作,例如在有一些應用中,Xml文件需要包含一些CSS文件用以應用樣式來渲染自身:

<?xml-stylesheet type="text/css" href="tutorials.css"?>

在QXmlStreamReader中,好像緊貼<?的被識別位Target,後面的內容都被認為是Content:

while (!m_xmlReader.isProcessingInstruction()) {
    m_xmlReader.readNext();
}
auto item = new QTreeWidgetItem(QStringList("[ProcessingInstruction]"));
item->setText(1, "target: " + m_xmlReader.processingInstructionTarget().toString() +
                 "    content: " + m_xmlReader.processingInstructionData().toString());
item->setBackgroundColor(1, QColor(Qt::yellow));
m_treeWidget.addTopLevelItem(item);

獲取PI相關內容需要分別調用QXmlStreamReader::processingInstructionTarget()QXmlStreamReader::processingInstructionData()

示例運行結果

因為Xml的元素是自定義的,這就意味著往往不同的Xml內部的元素結構都不一樣,不同的約定格式需要編寫不同的邏輯代碼處理。這個示例我們用QTreeWidget來展示解析出來的結構和內容:

技術分享圖片

完整代碼見鏈接。

Qt---Xml文件解析