使用SAX读取XML

SAX 事实上是公共领域中一种用于读取 XML 文挡的标准应用程序编程接口。Qt 的 SAX 类是对基于 SAX2 的 Java 实现的模拟,只是在命名上有些不太符合Qt的惯例。与 DOM 相比, SAX 更加底层但通常也更加快速。然而,由于在本章前面部分曾介绍过的 QXmlSimpleReader 类提供了一个更接近Qt风格的应用程序编程接口,且比 SAX 解析器更加快速,因此 SAX 解析器的主要用途就是

将使用SAX应用程序编程接口的代码导入Qt中。有关SAX更详细的信息,请参看http://www.saxproject.org/。

Qt提供了一个名为QXmlSimpleReader的基于SAX的非验证型XML解析器。这个解析器能够识别具有良好格式的XML文档并且支持XML文档的命名空间。当这个解析器遍历文档时,它调用注册的处理函数中的虚拟函数来表明解析事件。(这些"解析事件"和Qt事件并无联系,就像按键事件和鼠标事件一样)。我们假设这个解析器正在解析如下的XMT文档:

<doc><quote>Gnothi seauton</quote>
</doc>

解析器将会调用如下这些解析事件处理函数:

startDocument()
startElement("doc")
startElement("quote")
characters("Gnothi seauton")
endElement("quote")
endElement("doc")
endDocument()

上述的这些函数都是在QXmlContentHandler中声明过的。为了简单,我们省略了 startElement() 和 endElement()中的一些参数。

QXmlContentHandle只是可以和QXmISimpleReader协作使用的众多处理程序类之一。其他的还有QXmlEntityResolver、QXmlDTDHandler、QXmlErrorHandler、QXmIDeclHandler、QXmILexicalHandler。这些类仅仅声明纯虚函数并且给出不同类型的解析事件的相关信息。对于绝大多数应用程序来说只有QXmlContentHandler和QXmIErrorHandler是必要的。我们用到的类的层级关系如图16.4所示。

为了方便,Qt还提供了QXmIDefaultHandler,这是一个派生自所有处理程序类并且为所有函数都提供具体实现的类。含有很多抽象处理程序类和一个具体子类的这种设计构思,在Qt中并不常用,这里它被用来密切关注模型的Java实现。

与使用QXmlStreamReader或者DOM应用程序编程接口相比,使用SAX应用程序编程接口最显著的区别在于:SAX应用程序编程接口需要我们利用成员变量手动追踪解析器的状态,而其他两种采用考虑向下递归的方法则不需要。

为了闸明如何使用SAX读取XML文件,我们将为本章前面部分提到的书刊索引文件格式编写一个解析器。这里将使用一个QXmlSimpleReader和一个名为SaxHandler的QXmlDefaultHandler子类来解析。

实现解析器的第一步是定义子类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 &attributes);bool endElement(const QString &namespaceURI,const QString &localName,const QString &qName);bool characters(const QString &str);bool fatalError(const QXmlParseException &exception);private:QTreeWidget *treeWidget;QTreeWidgetItem *currentItem;QString currentText;
};

SaxHandler类派生自QXmlDefaultHander,并且重新实现4个函数:startElement()、endElement()、characters()、fatalError()。前面三个函数都是在QXmlContentHandler中声明的,最后一个函数则是在QXmlErrorHandler中声明的。

SaxHandler::SaxHandler(QTreeWidget *tree)
{treeWidget = tree;
}

SaxHandler构造函数接受用存储在 XML 文件中的信息进行组装的 QTreeWìdget。

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对象,同时创建一个QXmlInputSource以读取文件的内容。然后创建一个QXmlSimpleReader来解析这个文件。我们设置该类(SaxHandler)的阅读器内容和错误处理程序,接着对阅读器调用parse()来执行解析。在SaxHandler中仅重新实现了来自于QXmIContentHandler和QXmIErrorHandler类的函数。如果已经重新实现了来自于其他处理程序类的函数则还需要调用相应的setXxxHandler()函数。

我们传递一个QXmlInputSource而不是传递一个简单的QFile对象给parse()函数。这个类将打开并读取给定的文件(考虑了<?xml?>声明中指定的任意字符编码)给定的文件,同时它还提供了一个解析器读取文件的接口。

bool SaxHandler::startElement(const QString & /* namespaceURI */,const QString & /* localName */,const QString &qName,const QXmlAttributes &attributes)
{if (qName == "entry") {currentItem = new QTreeWidgetItem(currentItem ?currentItem : treeWidget->invisibleRootItem());currentItem->setText(0, attributes.value("term"));} else if (qName == "page") {currentText.clear();}return true;
}

当阅读器遇到一个新的打开标签时,就会调用startElement()函数。第三个参数是标签的名称(或者更加准确地说,是它的"限定名")。第四个参数是属性列表。在这个实例中,忽略了第一个和第二个参数。对于使用XML命名空间机制的XML文件,它们非常有用,这个主题会在参考文档中详细论述。

如果标签是<entry>,就创建一个新的QTreeWidgetltem项。如果标签嵌套在另一个<entry>中则新的标签将在这个索引中定义一个子条目,并且这个新的QTreeWidgetltem会作为代表包含条目的QTreeWidgetltem的子对象而创建。不然,就创建QTreeWidgetltem作为顶级项,使用树形窗口部件的不可见根项作为它的父对象。我们调用setText(),将第0列中显示的文本值设置为这个<entry>标签的term属性。

如果标签是<page>,就将currentText变量设置为一个空字符串。该变量作为一个累加器,用于<page></page>标签之间的文本。

最后我们返回true值让SAX继续解析这个文件。如果相把那此未知的标签也作为错误报告,这时就需要返回false值。然后,还可以在QXmIDefaulHandler中重新实现errorString(),以返回一个适当的出错消息。

bool SaxHandler::characters(const QString &str)
{currentText += str;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>就更新currentlten私有变量,使它指向当前QTreeWidgetltem的父对象。(因为一些历史原因,顶级项返回0值作为它们的父对象,而不是返回不可见的root项。)这样可以确保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文件失败时,就会调用fatalEror()函数。如果这种情况发生,我们仅向控制台输出一条出错信息给出行号列号以及这个解析器的错误文本。

这样就完成了SaxHandler类的实现。它的main()函数与前一节中解析DomParser的主函数几乎一样,唯一的区别在于这里使用的是SaxHandler而不是DomParser。

saxhandler.h

#ifndef SAXHANDLER_H
#define SAXHANDLER_H#include <QXmlDefaultHandler>class QTreeWidget;
class QTreeWidgetItem;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 &attributes);bool endElement(const QString &namespaceURI,const QString &localName,const QString &qName);bool characters(const QString &str);bool fatalError(const QXmlParseException &exception);private:QTreeWidget *treeWidget;QTreeWidgetItem *currentItem;QString currentText;
};#endif

saxhandler.cpp

#include <QtGui>
#include <iostream>#include "saxhandler.h"SaxHandler::SaxHandler(QTreeWidget *tree)
{treeWidget = tree;
}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);
}bool SaxHandler::startElement(const QString & /* namespaceURI */,const QString & /* localName */,const QString &qName,const QXmlAttributes &attributes)
{if (qName == "entry") {currentItem = new QTreeWidgetItem(currentItem ?currentItem : treeWidget->invisibleRootItem());currentItem->setText(0, attributes.value("term"));} else if (qName == "page") {currentText.clear();}return true;
}bool SaxHandler::characters(const QString &str)
{currentText += str;return true;
}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;
}bool SaxHandler::fatalError(const QXmlParseException &exception)
{std::cerr << "Parse error at line " << exception.lineNumber()<< ", " << "column " << exception.columnNumber() << ": "<< qPrintable(exception.message()) << std::endl;return false;
}

main.cpp

#include <QtGui>
#include <iostream>#include "saxhandler.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);QStringList args = QApplication::arguments();if (args.count() < 2) {std::cerr << "Usage: saxhandler file1.xml..." << std::endl;return 1;}QStringList labels;labels << QObject::tr("Terms") << QObject::tr("Pages");QTreeWidget treeWidget;treeWidget.setHeaderLabels(labels);treeWidget.header()->setResizeMode(QHeaderView::Stretch);treeWidget.setWindowTitle(QObject::tr("SAX Handler"));treeWidget.show();SaxHandler handler(&treeWidget);for (int i = 1; i < args.count(); ++i)handler.readFile(args[i]);return app.exec();
}

Qt4_使用SAX读取XML相关推荐

  1. 不积跬步无以至千里,不积小流无以成江海----SAX读取xml

    sax 读取xml import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.OutputFormat; imp ...

  2. 使用SAX读取XML

    使用SAX读取XML文件 1.原理 2.优缺点 3.解析步骤 1.生成一个person.xml文件 2.Person类 3.Myhandler类继承DefaultHandler并重写相关方法 4.运行 ...

  3. Qt4_使用QXmlStreamReader读取XML

    使用QXmlStreamReader读取XML 使用 QXmlStreamReader,是在 Qt 中读取 XML 文档的最快且最简单的方式.因为解析器的工作能力是逐渐递增的,所以它尤其适用于诸如查找 ...

  4. Qt4_用DOM读取XML

    用DOM读取XML DOM 是一种解析由万维网协会(W3C) 所开发的 XML 文档的标准应用程序编程接口. Qt 提供一套用于读取.操作和编写 XML 文挡的非验证型二级 DOM 实现. DOM 把 ...

  5. python 使用sax 解析xml 文件

    这里不是说xml 的所以如果xml 不了解,可以百度大致看下即可, SAX知识了解 SAX (simple API for XML )  有解析器和事件处理器 解析器负责读取XML文档,并向事件处理器 ...

  6. 使用SAX解析XML封装实体Bean

    新的项目需要对用户权限进行控制,经过和项目经理商量我们决定使用XML文件存储权限代码和层次关系,这样比较方便也便于维护,使用SAX读取XML文件,我发现在读取的时候可以顺便将XML文件中的内容封装为实 ...

  7. java读取xml文件报“org.xml.sax.SAXParseException: Premature end of file”

    背景:java读取xml文件,xml文件内容只有"<?xml version="1.0" encoding="UTF-8"?>" ...

  8. SAX解析XML文档——(二)

    SAX从上向下解析,一行一行解析.节省内存,不适合CRUD. XML文档: <?xml version="1.0" encoding="UTF-8"?&g ...

  9. JAVA SAX解析XML字符串实例

    2019独角兽企业重金招聘Python工程师标准>>> Demo代码如下: package com.wenqi.demo;import java.io.IOException; im ...

最新文章

  1. 30分钟看懂XGBoost的基本原理
  2. Java数据类型缓存池
  3. new和override
  4. 【数理知识】《矩阵论》方保镕老师-第5章-矩阵微积分及其应用
  5. 这次来个BGP反射器的简单小实验
  6. MySQL小问题:导入employee测试数据
  7. mysql 存储引擎版本_mysql不同版本和存储引擎选型的验证
  8. mysql 5.6.17 备份还原_mysql 备份和恢复
  9. 谈谈html5存储之IndexdDB
  10. java类加载体系,Java类加载体系
  11. 在线JSON转flow工具
  12. json日期格式化 java_java_Java Web程序中利用Spring框架返回JSON格式的日期,返回Json时格式化日期Date 第一 - phpStudy...
  13. LU分解的矩阵逆运算
  14. python对象、引用
  15. ubunt18 mysql_Ubuntu18.04下安装MySQL教程
  16. 2022/1/12(自闭半日游)
  17. 来了老弟,帅气模态框
  18. “腾源虎”表情包超萌上线,更有4000份定制红包封面免费送!
  19. 本周运气大爆发~希望不会把自己的好运都用光了……
  20. (论文笔记06.High Fidelity Data Reduction for Big Data Security Dependency Analyses(CCF A)2016)

热门文章

  1. Mysql主从占用大量cpu_Mysql占用过高CPU时的优化手段
  2. 攻防世界逆向——key
  3. 无法获得锁 /var/cache/apt/archives/lock – open (11 资源临时不可用)
  4. Silverlight中需要用到模板选择器(DataTemplateSelector)的替代方案
  5. android中dip、dp、px、sp和屏幕密度
  6. 计算机网络学习笔记(17. 计算机网络作业一)
  7. 零基础带你学习MySQL—Insert语句以及注意事项(七)
  8. python中双向索引_对索引Include子句的深入分析
  9. 预约挂号费用保险赔吗?
  10. 为什么现在很多公司和员工签订了合同之后都不给员工一份?