Qt---Xml文件解析
本文我們通過一個讀取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()
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文件解析