XML文件解析方式之一是SAX方式,SAX解析方式会逐行地去扫描XML文档,当遇到标签时会触发解析处理器,采用事件处理的方式解析XML (Simple API for XML) 。SAX是一个用于处理XML事件驱动的“推”模型,虽然不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。
SAX解析不像DOM那样建立一个完整的文档树,而是在读取文档时激活一系列事件,这些事件被推给事件处理器,然后由事件处理器提供对文档内容的访问。常见的事件处理器有三种基本类型:
- 用于访问XML DTD内容的DTDHandler;
- 用于低级访问解析错误的ErrorHandler;
- 用于访问文档内容的ContentHandler,这也是最普遍使用的事件处理器。
解析器读取输入文档并在处理文档时将每个事件推给文档处理器(MyContentHandler)。
与DOM相比,SAX解析器能提供更好的性能优势,它提供对XML文档内容的有效低级访问。SAX模型最大的优点是内存消耗小,因为整个文档无需一次加载到内存中,这使SAX解析器可以解析大于系统内存的文档。另外,你无需像在DOM中那样为所有节点创建对象。最后,SAX“推”模型可用于广播环境,能够同时注册多个ContentHandler,并行接收事件,而不是在一个管道中一个接一个地进行处理。
SAX的缺点是必须实现多个事件处理程序以便能够处理所有到来的事件,同时你还必须在应用程序代码中维护这个事件状态,因为SAX解析器不能交流元信息,如DOM的父/子支持,所以你必须跟踪解析器处在文档层次的哪个位置。如此一来,你的文档越复杂,你的应用逻辑就越复杂。虽然没有必要一次将整个文档加载到内存中,但SAX解析器仍然需要解析整个文档,这点和DOM一样。也许SAX面临的最大问题是它没有内置如XPath所提供的那些导航支持。再加上它的单遍解析,使它不能支持随机访问。这一限制也表现在名字空间上: 对有继承名字空间的元素不做注解。这些限制使SAX很少被用于操作或修改文档。
那些只需要单遍读取内容的应用程序可以从SAX解析中大大受益。很多B2B和EAI应用程序将XML用做封装格式,接收端用这种格式简单地接收所有数据。这就是SAX明显优于DOM的地方:因高效而获得高吞吐率。在SAX 2.0 中有一个内置的过滤机制,可以很轻松地输出一个文档子集或进行简单的文档转换。
一、SAX解析方式的步骤
SAX解析可分四个步骤进行:
- 得到xml文件对应的资源,可以是xml的输入流,文件或uri
- 得到SAX解析工厂(SAXParserFactory)
- 由解析工厂生产一个SAX解析器(SAXParser)
- 传入输入流和handler给解析器,调用parse()解析
二、SAX的常用接口介绍
1、ContentHandler接口
该接口封装了一些对事件处理的方法,当XML解析器开始解析XML输入文档时,它会遇到某些特殊的事件,比如文档的开头和结束、元素开头和结束、以及元素中的字符数据等事件。当遇到这些事件时,XML解析器会调用ContentHandler接口中相应的方法来响应该事件。ContentHandler接口的方法有以下几种:
void startDocument()
void endDocument()
void startElement(String uri, String localName, String qName, Attributes atts)
void endElement(String uri, String localName, String qName)
void characters(char[ ] ch, int start, int length)
2、ErrorHandler接口
ErrorHandler接口是SAX错误处理程序的基本接口。如果SAX应用程序需要实现自定义的错误处理,则它必须实现此接口,然后解析器将通过此接口报告所有的错误和警告。该接口的方法如下:
void error(SAXParseException exception)
void fatalError(SAXParseException exception)
void warning(SAXParseException exception)
3、DTDHandler接口
DTDHandler用于接收基本的DTD相关事件的通知,仅包括DTD事件的注释和未解析的实体声明部分。SAX解析器可按任何顺序报告这些事件,而不管声明注释和未解析实体时所采用的顺序;但是,必须在文档处理程序的startDocument()事件之后,在第一个startElement()事件之前报告所有的DTD事件。DTDHandler接口包括以下两个方法:
void startDocumevoid notationDecl(String name, String publicId, String systemId) nt()
void unparsedEntityDecl(String name, String publicId, String systemId, String notationName)
4、EntityResolver接口
EntityResolver接口是用于解析实体的基本接口,该接口只有一个方法,如下:
public InputSource resolveEntity(String publicId, String systemId)
解析器将在打开任何外部实体前调用此方法。此类实体包括在DTD内引用的外部DTD子集和外部参数实体和在文档元素内引用的外部通用实体等。如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口。
三、Xerces C++ SAX解析XML文档
1、编译xerces
https://blog.csdn.net/t18438605018/article/details/117478843
2、在工程中使用xerces
- 编译xerces成功后,在生成目录会生成xerces-c_3_1D.dll,xerces-c_3_1D.lib。(如果是release模式,就不会有后面的D)
- 新建VS工程,添加C++/include目录和link/xerces-c_3_1D.lib,拷贝xerces-c_3_1D.dll到工程生成目录
- 添加测试程序
// MySAXHandler.h
#pragma once
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/sax2/Attributes.hpp>
XERCES_CPP_NAMESPACE_USE
class MySAX2Handler : public DefaultHandler
{
public:
MySAX2Handler();
~MySAX2Handler();
public:
void startElement(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname,
const Attributes& attrs);
void characters(
const XMLCh* const chars,
const XMLSize_t length);
void endElement(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname);
void fatalError(const SAXParseException&);
};
// MySAXHandler.cpp
#include "MySAXHandler.h"
#include <iostream>
MySAX2Handler::MySAX2Handler()
{
}
MySAX2Handler::~MySAX2Handler()
{
}
void MySAX2Handler::startElement(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname,
const Attributes& attrs)
{
char* message = XMLString::transcode(qname);
std::cout << "start element: " << message << std::endl;
XMLString::release(&message);
}
void MySAX2Handler::fatalError(const SAXParseException& exception)
{
char* message = XMLString::transcode(exception.getMessage());
std::cout << "Fatal Error: " << message
<< " at line: " << exception.getLineNumber()
<< std::endl;
XMLString::release(&message);
}
void MySAX2Handler::characters(const XMLCh* const chars, const XMLSize_t length)
{
}
void MySAX2Handler::endElement(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname)
{
char* message = XMLString::transcode(qname);
std::cout << "end element: " << message << std::endl;
XMLString::release(&message);
}
// main.cpp
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/util/XMLString.hpp>
#include <iostream>
#include "MySAXHandler.h"
int main(int argc, char* args[])
{
try
{
XMLPlatformUtils::Initialize();
}
catch (const XMLException& toCatch)
{
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Error during initialization! :\n";
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
return 1;
}
const char* xmlFile = "./data/personal.xml";
SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
parser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true); // optional
parser->setFeature(XMLUni::fgXercesDynamic, false);
parser->setFeature(XMLUni::fgXercesSchema, true);
parser->setFeature(XMLUni::fgXercesSchemaFullChecking, true);
MySAX2Handler* defaultHandler = new MySAX2Handler();
parser->setContentHandler(defaultHandler);
parser->setErrorHandler(defaultHandler);
parser->setEntityResolver(defaultHandler);
try
{
parser->parse(xmlFile);
}
catch (const XMLException& toCatch)
{
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
return -1;
}
catch (const SAXParseException& toCatch)
{
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
return -1;
}
catch (...)
{
std::cout << "Unexpected Exception \n";
return -1;
}
//defaultHandler->OutputPsList();
delete parser;
delete defaultHandler;
XMLPlatformUtils::Terminate();
system("PAUSE");
return 0;
}
运行结果: