网站首页 > java教程 正文
一、SAX是什么?
SAX事实上是公共领域中一种用于读取XML文档的标准应用程序编程接口。Qt的SAX类是对基于SAX2的Java实现的模拟,只是在命名上有些不太符合Qt的惯例。与DOM相比,SAX更加底层但通常也更加快速。然而,由于在前面文章曾介绍过的QXmlSimpleReader类提供了一个更接近Qt风格的应用程序编程接口,且比SAX解析器更加快速,因此SAX解析器的主要用途就是将使用SAX应用程序编程接口的代码导入Qt中。
Qt提供了一个名为QXmlSimpleReader的基于SAX的非验证型XML解析器。这个解析器能够识别具有良好格式的XML文档并且支持XML文档的命名空间。当这个解析器遍历文档时,它调用注册的处理函数中的虚拟函数来表明解析事件。(这些“解析事件”和Qt事件并无联系,就像按键取件和鼠标事件一样)。我们假设这个解析器正在解析如下的文档:
<doc>
<quote>Gnothi seauton</quote>
</doc>
解析器将会调用如下这些解析事件处理函数:
startDocument()
startElement("doc")
startElement("quote")
characters("Gnothi seauton")
endElement(''quote”)
endElement("doc")
endDocument()
上述的这些函数都是在QXmlContentHandler中声明过的。为了简单,我们省略了startElement()和endElement()中的一些参数。
QXmlContentHandler只是可以和QXmlSimpleReader协作使用的众多处理程序类之一。其他的还有QXmlEntityResolver、QXmlDTDHandler、QXmlErrorHandler、QXmlDeclhandler和QXmlLexicalHandler。这些类仅仅声明纯虚函数并且给出不同类型的解析事件的相关信息。对于绝大多数应用程序来说,只有QXmlContentHandler和QXmlErrorHandler是必要的。这些类的层级关系如下图所示。
为了方便,Qt还提供了QXmlDefaultHandler,这是一个派生自所有处理程序类并且为所有函数都提供具体实现的类。含有很多抽象处理程序类和一个具体子类的这种设计构思,在Qt中并不常用,这里它被用来密切关注模型的java实现。
与使用QXmlStreamReader或者DOM应用程序接口相比,使用SAX最显著的区别在于:SAX需要我们利用成员变量手动追踪解析器的状态,而其他两种采用考虑向下递归的方法则不需要。
二、SAX读取XML文件实例
为了阐明如何使用SAX读取XML文件,我们将为前面文章提到的书刊索引文件格式编写一个解析器。这里将使用一个QXmlSimpleReader和一个名为SaxHandler的QXmlDefaultHandler子类来实现。程序运行界面如下:
1. SaxHandler类的定义
实现解析器的第一步是定义QXmlDefaultHandler的子类:
class SaxHandler : public QXmlDefaultHandler
{
public:
SaxHandler(QTreeWidget *tree);
bool readFile(const QString &fileName);
protected:
bool startElement(const QString &namespaceURI,
const QString &localName,
const QString &qName,
const QXmlAttributes &atts);
bool endElement(const QString &namespaceURI,
const QString &localName,
const QString &qName);
bool characters(const QString &ch);
bool fatalError(const QXmlParseException &exception);
private:
QTreeWidget *treeWidget;
QTreeWidgetItem *currentItem;
QString currentText;
};
SaxHandler类派生自QXmlDefaultHandler,并且重新实现了4个函数:startElement()、endElement()、characters()和fatalError()。前面二个函数都是在QXmlContentHandler中声明的,最后一个函数则是在QXmlErrorHandler中声明的。
2. SaxHandler类的实现
SaxHandler::SaxHandler(QTreeWidget *tree)
{
treeWidget=tree;
}
SaxHandler构造函数接受用存储在XML文件中的信息进行组装的QTreeWidget。
bool SaxHandler::readFile(const QString &fileName)
{
currentItem=0;
QFile file(fileName);
QXmlInputSource inputSource(&file);
QXmlSimpleReader reader;
reader.setContentHandler(this);
reader.setErrorHandler(this);
return reader.parse(inputSource);
}
当获得要解析文件的文件名时,就会调用这个函数。我们为文件创建一个QFile对象,同时创建一个QXmllnputSource以读取文件的内容。然后,创建一个QXmlSimpleReader来解析这个文件。我们设置该类(SaxHandler)的阅读器内容和错误处理程序,接着对阅读器调用parse()来执行解析。在SaxHandler中,仅从新实现了来自于QXmlContentHandler和QXmlErrorHandler类的函数。如果已经重新实现了来自于其他处理程序类的函数,则还需要调用相应的setXxxHandler()函数。
我们传递一个QXmlInputSource,而不是传递一个简单的QFile对象给parse()函数。这个类将打开并读取给定的文件(考虑了<?xml?>声明中指定的任意字符编码),同时它还提供了一个解析器读取文件的接口。
bool SaxHandler::startElement(const QString &namespaceURI,
const QString &localName, const QString &qName, const QXmlAttributes &atts)
{
if(qName=="entry"){
currentItem=new QTreeWidgetItem(currentItem ?
currentItem:treeWidget->invisibleRootItem());
currentItem->setText(0,atts.value("term"));
}else if (qName=="page") {
currentText.clear();
}
return true;
}
当阅读器遇到一个新的打开标签时,就会调用startElement()函数。第三个参数是标签的名称。第四个参数是属性列表。在这个实例中,忽略了第一和第二个参数。对于使用XML命名空间机制的XML文件,它们非常有用。
如果标签是<entry>,就创建一个新的QTreeWidgetItem项。如果标签嵌套在另一个<entry>中,则新的标签将在这个索引中定义一个子条目,并且这个新的QTreeWidgetItem会作为代表包含条目的QTreeWidgetItem的子对象而创建。不然,就创建QTreeWidgetItem作为顶级项,使用树形窗口的不可见根项作为它的父对象。我们调用setText(),将第0列中显示的文本值设置为这个<entry>标签的term属性。
如果标签是<page>,就将currentText变量设置为一个空字符串。该变量作为一个累加器,用于<page>和</page>标签之间的文本。
最后,我们返回true值让SAX继续解析这个文件。如果想把那些未知的标签也作为错误报告,这时就需要返回false值。然后,还可以在QXmlDefaultHandler中重新实现errorString(),以返回一个适当的出错信息。
bool SaxHandler::characters(const QString &ch)
{
currentText+=ch;
return true;
}
可以调用函数characters()报告XML文档中的字符数据。我们只把这些字符串添加到currentText变量中。
bool SaxHandler::endElement(const QString &namespaceURI,
const QString &localName, const QString &qName)
{
if(qName=="entry"){
currentItem=currentItem->parent();
}else if(qName=="page"){
if(currentItem){
QString allPages=currentItem->text(1);
if(!allPages.isEmpty())
allPages+=", ";
allPages+=currentText;
currentItem->setText(1,allPages);
}
}
return true;
}
当阅读器遇到一个关闭标签时,就会调用endElement()函数。就像startElement()一样,第三个参数是标签的名称。
如果标签是</entry>,就更新currentItem私有变量,使他指向当前QTreeWidgetItem的父对象。这样可以确保currentItem变量能恢复为相应的<entry>标签被读取之前的值。
如果标签为</page>,则把指定的页码或者页码范围添加到第一列当前项的文本中,以逗号分隔。
bool SaxHandler::fatalError(const QXmlParseException &exception)
{
std::cerr<<"Parse error at line "<<exception.lineNumber()
<<", "<<"column "<<exception.columnNumber()<<": "
<<qPrintable(exception.message())<<std::endl;
return false;
}
当阅读器解析XML文件失败吋,就会调用fatalError()函数。如果这种情况发生,我们仅向控制台输出一条出错信息,给出行号、列号以及这个解析器的错误文本。
这样就完成了SaxHandler类的实现。它的使用与前面的DomParser几乎一样,不在复述。
——————————————————
对于本文实例完整代码有需要的朋友,可关注并在评论区留言!
猜你喜欢
- 2024-10-17 Qt开发-DOM方式解析XML(qt开发工具)
- 2024-10-17 JAVA 操作笔记-XML(六)(xml在java中怎么用)
- 2024-10-17 作为一名程序猿,你不可不知的Java基础知识的三十个经典问答
- 2024-10-17 Android中XML文件解析,现在了解还不晚
- 2024-10-17 JSP 标准标签库(JSTL)(jsp标签库有哪些)
- 2024-10-17 Spring源码阅读:Spring XML解析机制
- 2024-10-17 XML文件(xml文件怎么打开)
- 2024-10-17 Python如何解析HTML和XML数据(python解析html xml最好的模块)
- 2024-10-17 深入解析Python中的XML处理:理论与实践的结合
- 2024-10-17 XML的解析方式(xml的解析方式有哪些)
你 发表评论:
欢迎- 05-15java使用iText解析PDF文件
- 05-15java 将pdf 形成的图片,每页一张图片 保存为pdf文件
- 05-15Java学习123——虚拟方法调用(Virtual Method Invocation)
- 05-15什么是JNI?为什么会有Native层?如何使用?
- 05-15Socket通信
- 05-15译文:理解Java中的弱引用
- 05-15Java 调用 DeepSeek 模型的完整示例及特点
- 05-15Java 对象和类
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)