用DOM读取XML

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

DOM 把 XML 文件表示成内存中的一棵树。我们可以按需要遍历这个 DOM 树,也可以修改这个树并把它作为 XML 文件保存到磁盘中。

让我们考虑如下这个,XML 文档:

<doc><quote>Scio me nihil scire</quote><translation>I know that I know nothing</translation>
</doc>

它对应如下所示的 DOM 树:

这个 DOM 树包含不同类型的节点。例如,Element 节点对应打开标签以及与它匹配的关闭标签。在这两个标签之间的内容则作为这个Element 节点的子节点出现。在 Qt 中,节点类型(和其他所有与 DOM 有关的类一样)具有一个 QDom 前缀。因此,QDomElement 就代表一个Elernent 节点,而QDomText 就代表一个 Text 节点。

不同类型的节点可以具有不同种类的子节点。例如,一个 Element 节点可以包含其他 Element
节点,也可以包含 EntityReference、Text、CDATASection、ProcessingInstruction 以及 Connnent 节点。

图16.3 给出了节点可以包含在的子节点的种类。图中显示为灰色的节点则不能拥有它自己的子节点。

为了演示如何使用 DOM 读取 XML 文件,我们将为前一节中提到的书刊索引文件格式编写一个解析器。

class DomParser
{public:DomParser(QTreeWidget *tree);bool readFile(const QString &fileName);private:void parseBookindexElement(const QDomElement &element);void parseEntryElement(const QDomElement &element,QTreeWidgetItem *parent);void parsePageElement(const QDomElement &element,QTreeWidgetItem *parent);QTreeWidget *treeWidget;
};

定义了一个称为 DomParse 的类,它将会解析 XML 书刊索引文档并且在 QTreeWidget 中显示结果。

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

在构造函数中,我们仅将给定的树形窗口部件赋给成员变量。所有的解析都在readFile() 函数的内部完成。

bool DomParser::readFile(const QString &fileName)
{QFile file(fileName);if (!file.open(QFile::ReadOnly | QFile::Text)) {std::cerr << "Error: Cannot read file " << qPrintable(fileName)<< ": " << qPrintable(file.errorString())<< std::endl;return false;}QString errorStr;int errorLine;int errorColumn;QDomDocument doc;if (!doc.setContent(&file, false, &errorStr, &errorLine,&errorColumn)) {std::cerr << "Error: Parse error at line " << errorLine << ", "<< "column " << errorColumn << ": "<< qPrintable(errorStr) << std::endl;return false;}QDomElement root = doc.documentElement();if (root.tagName() != "bookindex") {std::cerr << "Error: Not a bookindex file" << std::endl;return false;}parseBookindexElement(root);return true;
}

在readFile()中首先尝试打开那些文件名已经被传递进来的文件。如果有错误发生,就输出一个出错信息并返回false值表示文件打开失败。否则,就设置一此变量用以保存那些需要的解析出错信息然后创建一个QDomDocument。当对DOM文档调用setContent()函数时,由QIODevice提供的整个XML文档将被读取并解析。如果该文档还未打开setContent()函数将自动打开设备。 setConent()的false参数将禁用命名空间的处理。关于XML命名空间及其如何在Qt中处理,可以查阅QtXml的参考文档。

如果有错误发生,就输出一个出错信息并返回false值表示解析失败。如果解析成功。就对 QDomDocument调用documentElement()以获得它唯一的QDomElement子对象,同时检查它是否为<bookindex>元素。如果已经有<bookindex>元素了,就调用parseBookindexElement()来解析它。与前一节中介绍的内容相似,解析过程是使用向下递归方法来实现的。

void DomParser::parseBookindexElement(const QDomElement &element)
{QDomNode child = element.firstChild();while (!child.isNull()) {if (child.toElement().tagName() == "entry")parseEntryElement(child.toElement(),treeWidget->invisibleRootItem());child = child.nextSibling();}
}

在parseBookindexElement()中,遍历所有的子节点。我们希望每一个节点都是一个<entry>素那样的话就可以调用parseEntry()来解析每一个节点。我们忽略那些未知的节点,以使书刊索引格式在不阻止旧的解析器工作的情况下今后也能被扩展。所有的<entrv>节点都是<bookindex>节点的直接子对象,在组装的用于反映DOM树的窗口部件中它还是顶级节点。所以当我们想透一解析这些节点时,将传递节点元素和树的不可见根项以作为窗口部件树项的父对象。

QDomNode类可以存储任何类型的节点。如果想进一步处理一个节点,首先必须把它转换头正确的数据类型。在这个实例中,我们仅仅关心Element节点,所以对QDomNode调用toElement()以把它转换成QDomElement然后调用tagName()来取得元素的标签名称。如果节点不是Elemen类型那么toElement()函数就返回一个空QDomElement对象和一个空的标签。

void DomParser::parseEntryElement(const QDomElement &element,QTreeWidgetItem *parent)
{QTreeWidgetItem *item = new QTreeWidgetItem(parent);item->setText(0, element.attribute("term"));QDomNode child = element.firstChild();while (!child.isNull()) {if (child.toElement().tagName() == "entry") {parseEntryElement(child.toElement(), item);} else if (child.toElement().tagName() == "page") {parsePageElement(child.toElement(), item);}child = child.nextSibling();}
}

在parseEntryElement()中,我们创建一个树形窗口部件项。传入的父对象项既可以是树的不可见根项(如果这是一个顶级条目的话),也可以是其他的条目(如果它只是一个子条目的话)。我们调用setText()将显示在项的第一列中的文本设置为<entry>标签的tem属性值。

一旦已经初始化了QTreeWidgetItem就遍历对应于当前<entry>标签的QDomElement节点下的所有子节点。对于每一个<entry>标签的子元素,我们利用当前项作为第二个参数来递归调用 parseEntryElement()。然后,利用当前条目作为父对象,创建每一子节点的QTreeWidgetltem。如果子元素为<page>,就调用parsePageElement()。

void DomParser::parsePageElement(const QDomElement &element,QTreeWidgetItem *parent)
{QString page = element.text();QString allPages = parent->text(1);if (!allPages.isEmpty())allPages += ", ";allPages += page;parent->setText(1, allPages);
}

在parseElement( )中,对元素调用text()以获得 <page></page>。标签之间文本;然后,将文本添加到 QTreeWidgetItem 的第二列,这是一列以逗号分隔的页码列表。DomElement::text()函数遍历元素的所有子节点并连接存储在Text和CDATA节点中的所有文本。

现在看看如醉使用DomParser 类来解析文件:

int main(int argc, char *argv[])
{QApplication app(argc, argv);QStringList args = QApplication::arguments();...QTreeWidget treeWidget;...DomParser parser(&treeWidget);for (int i = 1; i < args.count(); ++i)parser.readFile(args[i]);return app.exec();
}

首先设立一个 QTreeWidget,然后再创建一个 DomParser。对于命令行中列出的每一个文件,都调用 DomParser::readFile()来打开并解析它,同时组装树形窗口部件。

与前面的例子相似,为了连接到QtXml库,需要将如下命令行添加到应用程序的 .pro 文件中:

QT += xml

就像这个实例所描绘的,尽管并不如使用QXmlStreamReader 那么方便,遍历一个 DOM 树也还是相当简单和直接的。频繁使用 DOM 的程序员会经常编写他们自己的高级封装函数来简化那些常规需求的操作。

domparser.h

#ifndef DOMPARSER_H
#define DOMPARSER_Hclass QDomElement;
class QString;
class QTreeWidget;
class QTreeWidgetItem;class DomParser
{public:DomParser(QTreeWidget *tree);bool readFile(const QString &fileName);private:void parseBookindexElement(const QDomElement &element);void parseEntryElement(const QDomElement &element,QTreeWidgetItem *parent);void parsePageElement(const QDomElement &element,QTreeWidgetItem *parent);QTreeWidget *treeWidget;
};#endif

domparser.cpp

#include <QtGui>
#include <QtXml>
#include <iostream>#include "domparser.h"DomParser::DomParser(QTreeWidget *tree)
{treeWidget = tree;
}bool DomParser::readFile(const QString &fileName)
{QFile file(fileName);if (!file.open(QFile::ReadOnly | QFile::Text)) {std::cerr << "Error: Cannot read file " << qPrintable(fileName)<< ": " << qPrintable(file.errorString())<< std::endl;return false;}QString errorStr;int errorLine;int errorColumn;QDomDocument doc;if (!doc.setContent(&file, false, &errorStr, &errorLine,&errorColumn)) {std::cerr << "Error: Parse error at line " << errorLine << ", "<< "column " << errorColumn << ": "<< qPrintable(errorStr) << std::endl;return false;}QDomElement root = doc.documentElement();if (root.tagName() != "bookindex") {std::cerr << "Error: Not a bookindex file" << std::endl;return false;}parseBookindexElement(root);return true;
}void DomParser::parseBookindexElement(const QDomElement &element)
{QDomNode child = element.firstChild();while (!child.isNull()) {if (child.toElement().tagName() == "entry")parseEntryElement(child.toElement(),treeWidget->invisibleRootItem());child = child.nextSibling();}
}void DomParser::parseEntryElement(const QDomElement &element,QTreeWidgetItem *parent)
{QTreeWidgetItem *item = new QTreeWidgetItem(parent);item->setText(0, element.attribute("term"));QDomNode child = element.firstChild();while (!child.isNull()) {if (child.toElement().tagName() == "entry") {parseEntryElement(child.toElement(), item);} else if (child.toElement().tagName() == "page") {parsePageElement(child.toElement(), item);}child = child.nextSibling();}
}void DomParser::parsePageElement(const QDomElement &element,QTreeWidgetItem *parent)
{QString page = element.text();QString allPages = parent->text(1);if (!allPages.isEmpty())allPages += ", ";allPages += page;parent->setText(1, allPages);
}

main.cpp

#include <QtGui>
#include <iostream>#include "domparser.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);QStringList args = QApplication::arguments();if (args.count() < 2) {std::cerr << "Usage: domparser 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("DOM Parser"));treeWidget.show();DomParser parser(&treeWidget);for (int i = 1; i < args.count(); ++i)parser.readFile(args[i]);return app.exec();
}

Qt4_用DOM读取XML相关推荐

  1. 【Qt】DOM读取XML文档

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. XML文档示例 04. DOM读取XML文档内容 05. 预留 06. 附录 01. 概述 DOM(Document Ob ...

  2. [Qt教程] 第27篇 XML(一)使用DOM读取XML文档

    [Qt教程] 第27篇 XML(一)使用DOM读取XML文档 楼主  发表于 2013-5-21 21:14:28 | 查看: 1001| 回复: 14 使用DOM读取XML文档 版权声明 该文章原创 ...

  3. Qt4_使用SAX读取XML

    使用SAX读取XML SAX 事实上是公共领域中一种用于读取 XML 文挡的标准应用程序编程接口.Qt 的 SAX 类是对基于 SAX2 的 Java 实现的模拟,只是在命名上有些不太符合Qt的惯例. ...

  4. Qt4_使用QXmlStreamReader读取XML

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

  5. 使用DOM读取和维护XML数据

    笔记目录: 1.了解XML解析 2.使用DOM读取和维护XML数据 3.使用DOM4J读取和维护XML数据 1.DOM解析XML的步骤 2.使用DOM解析XML时主要使用的对象 XML中节点有三种:1 ...

  6. Qt 读取XML文档的简单示例

    目录名字 XML简介 Qt 操作XML文档一般方法 使用DOM读取XML文档 示例代码:读取XML文档 XML简介 XML(Extensible Markup Language,可扩展标记语言)是一种 ...

  7. java dom创建xml文件_Java 如何使用dom方式读取和创建xml文件

    Java 如何使用dom方式读取和创建xml文件 发布时间:2020-11-11 17:08:31 来源:亿速云 阅读:101 作者:Leah 本篇文章给大家分享的是有关Java 如何使用dom方式读 ...

  8. java使用org.w3c.dom解析XML文档,创建、增删查改,保存,读取,遍历元素等操作

    全栈工程师开发手册 (作者:栾鹏) java教程全解 java使用org.w3c.dom(java dom)解析XML文档,创建.增删查改,保存,读取,遍历元素等操作 在保存文件时需要载入crimso ...

  9. matlab分析xml文件_如何在Java中读取XML文件(DOM分析器)

    matlab分析xml文件 Today we will learn how to read the XML file in Java. We will also learn how to parse ...

最新文章

  1. leetcode--笔记——120. 三角形最小路径和
  2. Windows server 2012 搭建×××图文教程(二)配置路由和远程访问服务
  3. ruby File类
  4. 正态分布随机数 C语言,C语言产生满足正态分布的随机数
  5. +1.1向量 链表 数组
  6. 一个程序员的感慨的《虚拟光驱》
  7. 【优化】C#利用ODP.NET往oracle中高效插入百万数据
  8. 华为的型号命名规则_iQOO新品入网;三星Galaxy S21首张官方渲染图曝光;华为nova8真机曝光...
  9. 在Openstack上创建并访问Kubernetes集群
  10. c++builder Form重载WindowProc、WndProc 截获消息
  11. 拦截器inceptor
  12. Stata资源:一些不错的Stata-Blogs
  13. 【算法竞赛学习笔记】弦图和区间图
  14. PS如何删除智能图层为可编辑状态
  15. 平板无线网无法连接网络连接服务器,平板电脑可以连接无线网络但上不了网的解决方法...
  16. 虚拟机与宿主机之间的网络共享问题
  17. 【毕业季·进击的技术er】这三年来的风风雨雨,喜怒哀乐,坎坎坷坷,朝朝暮暮……都是那么美好,那么令人难以忘记。
  18. 如何在html里面搜索代码,怎样在网页里查找关键字
  19. 华为MCR·第4篇:客户档案管理管什么
  20. c++:vector sort()排序

热门文章

  1. 传输层端口号的范围是多少?被分为哪两部分_6.传输层协议
  2. 深度学习中激活函数的优缺点
  3. tail 命令只查看日志中的关键字所在行信息
  4. C# MVC Controller依赖注入的办法
  5. nmon在linux系统中的应用
  6. MSSQL OPTION语句详解
  7. python模块之logging
  8. 一个成功的Jsp程序员该怎样学习JSP呢?
  9. kubectl查看kubernetes运行信息
  10. kubernetes之四:存储