QT開發(四十三)——SAX方式解析XML

QT開發(四十三)——SAX方式解析XML

一、SAX簡介

SAXSimple API for XML的簡寫,是一種解析XML文件的替代方法,不是由W3C官方所提出的標準,是一種事件驅動的XML API,接近於底層,速度較快,但不便於隨機訪問任意節點。  

SAX解析的核心是事件處理機制,具有佔用內存少,效率高等特點。

SAX採用事件機制的方式來解析XML文檔。使用SAX解析器對XML文檔進行解析時SAX解析器根本不創建任何對象,只是在遇到XML文檔的各種標籤如文檔開始、元素開始、文本、元素結束時觸發對應的事件,並將XML元素的內容封裝成事件傳出去。而程序員則負責提供事件監聽器來監聽這些事件,從而觸發相應的事件處理方法,並通過這些事件處理方法實現對XML文檔的訪問

SAX解析事件一共有4種,需要分別設置4種監聽器:

A、ContentHandler:監聽XML文檔內容處理事件的監聽器

B、DTDHander:監聽DTD處理事件的監聽器

C、EntityResolver:監聽實體處理事件的監聽器

D、ErrorHandler:監聽解析錯誤的監聽器

QT的QtXml模塊中提供了一個基於SAXXML解析器QXmlSimpleReader類。當解析器解析一個XML的元素時,解析器會依次調用如下事件處理函數:startElement(),characters(),endElement()。可以在 startElement()中獲得元素名(如“title”)和屬性,在characters()中獲得元素中的文本(如“Qt”),在endElement()中進行一些結束讀取該元素時想要進行的操作。而所有的這些事件處理函數都可以通過繼承QXmlDefaultHandler類來重寫。

二、QXmlSimpleReader

1QXmlSimpleReader簡介

    QXmlSimpleReader類提供了簡單XML解析器的實現。

XML讀取器適合大部分應用程序,能夠解析良構XML,但不能解析任何外部實體。

QXmlSimpleReader類最容易的使用模式是創建一個實例,定義一個輸入源,指定讀取器使用的處理器,解析數據。

當讀取器遇到內容中的某種類型或是輸入源中發現錯誤,處理器會採取行動。讀取器必須被告知需要哪一種事件類型的處理器。對於大多數應用程序來說,可以自定義一個繼承於QXmlDefaultHandler類的處理器類,使用自定義的處理器處理錯誤和內容事件。

如果至少連錯誤和內容處理器都沒有設置,解析器將會默認什麼都不做。

處理輸入源最方便的方式是使用帶指定輸入源參數的parse()函數以單通道的方式讀取輸入源。如果一次讀取不能解析整個輸入源(比如XML文件太大或是正在網絡上傳輸),XML文件可以被解析器分塊讀取。分塊讀取解析可以通過告知prase()函數以遞增方式工作,隨後調用parseContinue()函數,直到所有數據被解析完成來實現。

    實現遞增解析通常的方式是連接信號readyRead()到網絡應答的槽函數,並且處理收到的數據。

    解析行爲可以通過使用setFeature()函數和setProperty()函數進行調整。

2QXmlSimpleReader成員函數

[virtual] void QXmlSimpleReader::setContentHandler(QXmlContentHandler *handler)

設置內容處理器到handler

[virtual] void QXmlSimpleReader::setErrorHandler(QXmlErrorHandler *handler)

設置錯誤處理器到handler,如果handler爲0,清空錯誤處理器

[virtual] void QXmlSimpleReader::setDTDHandler(QXmlDTDHandler *handler)

[virtual] void QXmlSimpleReader::setDeclHandler(QXmlDeclHandler *handler)

[virtual] void QXmlSimpleReader::setEntityResolver(QXmlEntityResolver *handler)

[virtual] void QXmlSimpleReader::setLexicalHandler(QXmlLexicalHandler *handler)

 

[virtual] void QXmlSimpleReader::setFeature(const QString &name, bool enable)

[virtual] void QXmlSimpleReader::setProperty(const QString &name, void *value)

 

[virtual] QXmlErrorHandler *QXmlSimpleReader::errorHandler() const

返回錯誤處理器,如果沒有設置,返回0

[virtual] QXmlContentHandler *QXmlSimpleReader::contentHandler() const

返回內容處理器,如果沒有設置,返回0

[virtual] bool QXmlSimpleReader::hasFeature(const QString &name) const

如果讀取器有名爲name的特性,返回true

三、QXmlDefaultHandler

1、QXmlDefaultHandler簡介

    QXmlDefaultHandler類提供了所有XML處理器類的實現,集成了特定處理器類的特性,使QXmlReader子類自定義處理器類有了一個便捷的切入點,特別是QXmlSimpleReader類。從基類中繼承的虛函數都會在QXmlDefaultHandler類中重寫。通過繼承QXmlDefaultHandler類,重寫相關虛函數,開發人員能夠專注於處理器相關部分的實現。

    在解析期間,XML讀取器必須被告知要使用哪一種事件的處理器。這就意味着,QXmlDefaultHandler雖然提供了從基類繼承來的函數的默認實現,我們仍然可以對特殊的事件使用指定的處理器。

    例如,QXmlDefaultHandler繼承了QXmlContentHandlerQXmlErrorHandler,因此通過繼承QXmlDefaultHandler,我們可以使用QXmlContentHandlerQXmlErrorHandler的處理器。

xmlReader.setContentHandler(handler);

xmlReader.setErrorHandler(handler);

    由於讀取器將會通知解析錯誤的處理器,需要重寫QXmlErrorHandler::fatalError()函數,當錯誤發生時需要停止解析。

bool Handler::fatalError (const QXmlParseException & exception)

  {

      qWarning() << "Fatal error on line" << exception.lineNumber()

                 << ", column" << exception.columnNumber() << ':'

                 << exception.message();

      return false;

  }

    fatalError()函數返回false會告訴解析器停止解析,。要繼續使用同一個讀取器需要創建一個新的處理器實例,設置使用它的讀取器。

    審查從QXmlDefaultHandler類繼承來的某些函數和思考爲什麼在自定義處理器類中要重寫這些函數是有用的。自定義處理器類爲了準備新內容的處理器通常會重寫QXmlContentHandler::startDocument()函數。通過重寫QXmlContentHandler::startElement(), QXmlContentHandler::endElement(), and QXmlContentHandler::characters()函數,文檔元素和文本會被處理。一旦文檔已經完全被讀取,需要重寫QXmlContentHandler::endDocument()函數爲了在內容上做某些終結或是確認。

2QXmlDefaultHandler成員函數

[virtual] bool QXmlDefaultHandler::characters(const QString &ch)

當解析完了字符數據中的垃圾數據時,讀取器會調用本函數

[virtual] bool QXmlDefaultHandler::comment(const QString &ch)

 

[virtual] bool QXmlDefaultHandler::endElement(const QString &namespaceURI, const QString &localName, const QString &qName)

 

[virtual] bool QXmlDefaultHandler::error(const QXmlParseException &exception)

 

[virtual] QString QXmlDefaultHandler::errorString() const

 

[virtual] bool QXmlDefaultHandler::fatalError(const QXmlParseException &exception)

 

[virtual] bool QXmlDefaultHandler::processingInstruction(const QString &target, const QString &data)

 

[virtual] bool QXmlDefaultHandler::startDocument()

 

[virtual] bool QXmlDefaultHandler::startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts)

 

 

[virtual] bool QXmlDefaultHandler::endDocument()

 

[virtual] bool QXmlDefaultHandler::endEntity(const QString &name)

 

[virtual] bool QXmlDefaultHandler::endPrefixMapping(const QString &prefix)

 

[virtual] bool QXmlDefaultHandler::startEntity(const QString &name)

 

[virtual] bool QXmlDefaultHandler::startPrefixMapping(const QString &prefix, const QString &uri)

void characters(char[] ch,int start,int length)

SAX解析器處理字符數據時觸發該方法

void endDocument():

SAX解析器處理文檔結束時觸發該方法

void endElement(String uri,String localName,String qName):

SAX解析器處理元素結束時觸發該方法

void endPrefixMapping(String prefix)

SAX解析器處理元素裏命名空間屬性(即xmlns:prefix屬性)結束時觸發該方法

void ignorableWhitesapce(char[] ch,int start,int length)

SAX解析器處理元素內容中可忽略的空白時觸發該方法

void skippedEntity(String name)

SAX解析器跳過實體時觸發該方法

void startDocument():

SAX解析器開始處理文檔時觸發該方法

void startElement(String uri,String localName,String qName,Attributes atts):

SAX解析器開始處理元素時觸發該方法

void startPrefixMapping(String prefix,String uri)

SAX解析器開始處理元素裏命名空間屬性(即xmlns:prefix屬性)時觸發該方法

void parse(InputSource input)

解析InputSource輸入源中的XML文檔

void parse(String systemId)

解析系統URI所代表的XML文檔

 

四、SAX讀取XML文檔實例

1QXmlSimpleReader解析器解析過程

    QT中提供了一個基於SAX的簡單的XML解析器QXmlSimpleReader,QXmlSimpleReader解析器需要QXmlInputSource爲其提供數據,QXmlInputSource會使用相應的編碼來讀取XML文檔的數據。在進行解析之前,還需要使用setContentHandler()來設置事件處理器,使用setErrorHandler()來設置錯誤處理器,使用參數this, 表明使用本類作爲處理器,也就是在解析過程中出現的各種事件都會使用本類的startElement()等事件處理函數來進行處理,而出現錯誤時會使用本類的fatalError()函數來處理。最後,調用了parse()函數來進行解析,parse()函數會在解析成功時返回true,否則返回false。

2、工程實例

建立工程,在工程文件中添加XML模塊支持。

自定義繼承自QXmlDefaultHandler的類,類名爲SAX_XML。重寫QXmlDefaultHandler類的startElement()、endElement()、characters()和fatalError()函數,定義readXML()函數用來讀入XML文件,QListWidget部件用來顯示解析後的XML文檔內容,currentText字符串變量用於暫存字符數據。

SAX_XML類文件如下:

sax_xml.h文件:

#ifndef SAX_XML_H
#define SAX_XML_H
#include <QXmlDefaultHandler>
#include <QListWidget>
#include <QString>
 
class SAX_XML : public QXmlDefaultHandler
{
public:
    SAX_XML();
    ~SAX_XML();
    bool readXML(const QString & fileName);
protected:
    bool characters(const QString &ch);
    bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName);
    bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts);
    bool fatalError(const QXmlParseException &exception);
private:
    QListWidget *list;
    QString currentText;
};
 
#endif // SAX_XML_H

sax_xml.cpp文件:

#include "sax_xml.h"
#include <QDebug>
 
SAX_XML::SAX_XML()
{
    list = new QListWidget;
    list->show();
}
 
bool SAX_XML::readXML(const QString &fileName)
{
    QFile file(fileName);
    // 讀取文件內容
    QXmlInputSource inputSource(&file);
    // 建立QXmlSimpleReader對象
    QXmlSimpleReader reader;
    // 設置內容處理器
    reader.setContentHandler(this);
    // 設置錯誤處理器
    reader.setErrorHandler(this);
    // 解析文件
    return reader.parse(inputSource);
}
 
bool SAX_XML::characters(const QString &ch)
{
    currentText = ch;
    return true;
}
 
bool SAX_XML::endElement(const QString &namespaceURI, const QString &localName, const QString &qName)
{
    if (qName == "title" || qName == "author")
        list->addItem("        " + qName + " : " + currentText);
    return true;
}
 
bool SAX_XML::startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts)
{
    if (qName == "library")
        list->addItem(qName);
    else if (qName == "book")
        list->addItem("    " + qName + atts.value("id"));
    return true;
}
 
bool SAX_XML::fatalError(const QXmlParseException &exception)
{
    qDebug() << exception.message();
    return false;
}
 
 SAX_XML::~SAX_XML()
 {
     delete list;
 }

 

Main.cpp文件:

#include "sax_xml.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    SAX_XML reader;
    reader.readXML("test.xml");
 
    return a.exec();
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章