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();
}